이예제에서는 OPC UA add-in을활성화하고 RoboDK를 OPC UA 서버로변환하는방법을배웁니다. UaExpert 소프트웨어와벡호프트윈캣3 TF6100을사용하여몇가지설정을살펴봅니다.
OPC UA add-in을사용하면서버포트와같은일부설정을구성할수있습니다. 서버를활성화하거나비활성화하거나 RoboDK로자동시작하도록선택할수도있습니다.
OPC UA Add-in이활성화된상태에서 OPC UA-OPC-UA 설정을 선택하여 OPC UA 설정을구성합니다.
다음이미지와같이왼쪽에 OPC UA 설정화면이표시됩니다.
"포트 4840에서실행중인 RoboDK의 OPC UA 서버"와같은메시지가표시되면 RoboDK의 OPC UA 서버가시작되었다는의미입니다.
하나이상의로봇이있는모든 RoboDK 스테이션에서 OPC UA 연결을테스트할수있습니다.
UaExpert 소프트웨어를사용하여 RoboDK OPC UA 서버와의연결을테스트할수있습니다.
Unified Automation 웹사이트(https://www.unified-automation.com/downloads/opc-ua-clients.html)에서무료버전의 UaExpert 소프트웨어를다운로드할수있습니다.
UaExpert를실행하고 "+" 버튼을클릭하여 RoboDK OPC UA 서버를추가합니다.
사용자지정검색을확장하고 <두 번 클릭하여 서버 추가> 옵션을선택하여 RoboDK OPC UA 서버를추가합니다.
이전 단계에서 구성한 OPC UA 서버의 URL인 opc.tcp://127.0.0.1:48440을 입력합니다.
보안이 "없음"인 OPC UA 서버를연결합니다.
서버가구성되었습니다.
이제 UaExpert에서 RoboDK OPC UA 서버에연결할수있습니다.
연결이설정되면노드및메소드를볼수있습니다.
RoboDK OPC UA 서버내부에는스테이션에대한몇가지기본정보를교환할수있는몇가지노드가있습니다.
RoboDK 노드는 RoboDK 소프트웨어의실제버전을제공하는노드입니다.
이예제에서는 RoboDK 64비트 v5.5.3.23031 버전이사용되었습니다.
시뮬레이션 속도는 실제 시뮬레이션 속도를 표시하고 사용자가 현재 시뮬레이션 속도를 덮어쓸 수 있는 노드입니다.
노드 값은 시뮬레이션 속도의 슬라이드 바를 참조합니다.
이 노드에서 현재 시뮬레이션을 읽을 수 있으며 시뮬레이션 속도를 덮어쓸 수 있습니다.
스테이션노드는사용자가 RoboDK에서스테이션의현재이름을가져올수있는노드입니다.
아래에서볼수있듯이스테이션노드는 RoboDK에서 "스테이션이름"으로참조됩니다.
스테이션파라미터와스테이션값은사용자가스테이션내부의모든파라미터를가져오거나설정할수있는쌍으로구성된노드입니다. RoboDK OPC UA 서버는 "StationParameter"의실제값을지속적으로모니터링하고스테이션값노드에서해당 "StationParameter"의값을반환합니다.
Station 파라미터를보려면 RoboDK Station > Station 파라미터를우클릭하세요.
상수매개변수필드에서기본스테이션매개변수와해당값을확인할수있습니다.
스테이션매개변수는 '매개변수' 필드를참조하고스테이션값은 '값' 필드를참조합니다.
그리고 '추가' 버튼을클릭하여자신만의파라미터를만들수있습니다.
새로운스테이션매개변수가추가됩니다.
파라미터이름과파라미터값을입력한다음적용을눌러저장합니다.
자신만의스테이션파라미터를설정할수도있습니다.
노드시간은 RoboDK 스테이션의현재시간을얻을수있는노드입니다.
DataTime 형식의값이반환됩니다.
그리고이노드는지속적으로업데이트됩니다.
RoboDK OPC UA 서버에는사용자가 RoboDK 스테이션의데이터에동적으로액세스할수있는몇가지방법도제공됩니다.
메서드> 호출을마우스오른쪽버튼으로클릭하여메서드를실행할수있습니다.
getItem은사용자가아이템의포인터를가져올수있는메서드입니다.
InputArguments의경우장치이름은필수이며, 장치이름은스테이션이름, 로봇이름등을이미지화할수있습니다. 그리고 Item ID는해당장치의포인터를반환하는 OutputArguments입니다.
이예제에서는 ABB 로봇의아이템 ID(포인터)가 "ABB_RB1"로지정되었습니다.
아이템이름이유효하지않거나스테이션에존재하지않는경우 0이반환됩니다.
getJonits는사용자가아이템 ID를기반으로스테이션에서로봇의조인트값을가져올수있는메서드입니다.
아이템 ID는아이템의포인터값으로, getItem() 메서드에서가져올수있습니다.
이 "ABB_RB1" 아이템이름으로아이템 ID를가져오고 UInt64 값을반환합니다.
조인트값은이전메서드에서가져온항목 ID를전달하면서반환됩니다.
getJointsStr은사용자가문자열값을기반으로조인트값을가져올수있는메서드입니다.
이메서드에서로봇이름(문자열)을전달할수있습니다.
내스테이션에서 ABB_RB1은내로봇의이름입니다.
로봇이름매개변수에 "ABB_RB1"을전달하고메서드를호출하면문자열형식의조인트값이반환됩니다.
setJointsStr은사용자가문자열값을기반으로로봇의조인트값을설정할수있는메서드입니다.
로봇이름에는 ABB_RB1이전달되며, 조인트매개변수에는조인트값이포함된문자열을전달하면됩니다.
For example:-0.000000,0.000000,-0.000000,-0.000000,-0.0,-0.000000
Beckhoff TwinCAT 3 소프트웨어를사용하여 RoboDK OPC UA 서버와의연결을테스트할수있습니다.
I/O>장치>새 항목 추가를 선택하여 OPC UA 클라이언트를 생성하여 시작할 수 있습니다.
OPC > 확인에서 가상 OPC UA 장치를 선택합니다.
OPC UA Virtual이삽입됩니다.
RoboDK OPC UA 서버에액세스하려면 OPC UA 클라이언트를추가해야합니다.
장치 1 > 마우스오른쪽버튼클릭 > 새항목추가를선택합니다.
"OPC UA 클라이언트(모듈)"를선택하고확인을선택합니다.
OPC UA 클라이언트가삽입됩니다.
OPC UA 클라이언트를열고 > 설정탭으로이동한후 > "엔드포인트선택"을클릭하여액세스하려는 OPC UA 서버엔드포인트를구성합니다.
RoboDK OPC UA 서버 URL을입력하고업데이트합니다.
"노드추가"를눌러 OPC UA 서버내부에있는노드를찾습니다.
트윈캣과 OPC UA 서버 간의 연결이 설정되면, OPC UA 서버의 세부 정보를 찾아볼 수 있습니다.
모든방법을선택하고확인을선택합니다.
메소드가구성에삽입됩니다.
이필드에서이름접두사를구성합니다.
트윈캣에서 PLC 코드를생성하려면 "Create Plc Code"를누르세요.
프로젝트에 OpcUaClient 폴더가생성되고모든 RoboDK 메서드는 IEC61131-3 기능블록형식으로생성됩니다.
이섹션에서는 RoboDK OPC UA 서버와통신하는 Beckhoff TwinCAT PLC의샘플프로그램을보여줍니다.
프로그램 주요 내용
VAR
bConnected :BOOL;
스테이션 포인터 :DINT;
iStep :INT;
bStart :BOOL;;
i INT;
TON TON;
bReset :BOOL;
bWrite :BOOL;
TON2 :TON;
bShow :BOOL:=TRUE;
bVis :BOOL:=True;
END_VAR
VAR
로봇_이름 :문자열(80):='abb_rb1';
Item_ID :ULINT;
arrJoints :array[0..11]의 lreal;
strJoints :문자열(80):='';
arrJointsFromStr:ARRAY[1..11]OF LREAL;
sSeparator :문자열(1) := ',';
arrJointsCommand:ARRAY[1..11]OF LREAL;
strJointsCommand:STRING(80);
END_VAR
VAR 상수
cStepWaitCmd :INT:=0;
cStepInit :INT:=5;
cStepGetItem :INT:=10;
cStepGetItemReset :INT:=20;
cStepGetItemError :INT:=990;
cStepGetJoints :INT:=30;
cStepGetJointsReset :INT:=40;
cStepGetJointsError :INT:=991;
cStepGetJointsStr :INT:=50;
cStepGetJointsStrReset:INT:=60;
cStepGetJointsStrError:INT:=992;
cStepSetJointStrDelay :INT:=69;
cStepSetJointsStr :INT:=70;
cStepSetJointsStrReset:INT:=80;
cStepSetJointsStrError:INT:=993;
cStepEnd :INT:=300;
cStepWaitReset :INT:=999;
END_VAR
VAR
aSplit :ARRAY[1..11] OF STRING(80);
bResultSplit :BOOL;
debug :BOOL;
URL :STRING:='http://192.168.3.42:8091';
END_VAR
bConnected:=OPCUA_VirtualClient_RoboDK_Station.bConnected;
사례 i단계
cStepWaitCmd:
IF bStart THEN
iStep:=cStepInit;
bStart:=FALSE;
END_IF
cStepInit:
StationPointer:=0;
FOR i :=1 TO 11 DO
arrJoints[i]:=0.0;
arrJointsFromStr[i]:=0.0;
aSplit[i]:='';
END_FOR
OPCUA_VirtualClient_RoboDK_Station.getItem.bBusy가 아닌 경우
그리고 OPCUA_VirtualClient_RoboDK_Station.getItem.bError가 아닙니다.
그리고 OPCUA_VirtualClient_RoboDK_Station.getJoints.bBusy가 아닙니다.
그리고 OPCUA_VirtualClient_RoboDK_Station.getJoints.bError가 아닙니다.
그리고 OPCUA_VirtualClient_RoboDK_Station.getJointsStr.bBusy가 아닙니다.
그리고 OPCUA_VirtualClient_RoboDK_Station.getJointsStr.bError가 아닙니다.
그리고 OPCUA_VirtualClient_RoboDK_Station.setJoints.bBusy가 아닙니다.
그리고 OPCUA_VirtualClient_RoboDK_Station.setJoints.bError가 아닙니다.
그리고 OPCUA_VirtualClient_RoboDK_Station.setJointsStr.bBusy가 아닙니다.
그리고 OPCUA_VirtualClient_RoboDK_Station.setJointsStr.bError가 아닙니다.
그때
iStep:=cStepGetItem;
END_IF
iStep:=cStepGetItem;
cStepGetItem:
IF OPCUA_VirtualClient_RoboDK_Station.getItem.bDone THEN
iStep:=cStepGetItemReset;
Item_ID:=OPCUA_VirtualClient_RoboDK_Station.getItem.Item_ID;
ELSIF OPCUA_VirtualClient_RoboDK_Station.getItem.bError THEN
iStep:=cStepGetItemError;
END_IF
cStepGetItemReset:
OPCUA_VirtualClient_RoboDK_Station.getItem.bError가 아닌 경우
그리고 OPCUA_VirtualClient_RoboDK_Station.getItem.bBusy가 아닙니다.
그때
iStep:=cStepGetJoints;
END_IF
cStepGetJoints:
IF OPCUA_VirtualClient_RoboDK_Station.getJoints.bDone
그리고 OPCUA_VirtualClient_RoboDK_Station.getJoints.bBusy가 아닙니다.
그때
iStep:=cStepGetJointsReset;
ELSIF OPCUA_VirtualClient_RoboDK_Station.getJoints.bError THEN
iStep:=991;
END_IF
cStepGetJointsReset:
OPCUA_VirtualClient_RoboDK_Station.getItem.bError가 아닌 경우
그리고 OPCUA_VirtualClient_RoboDK_Station.getItem.bBusy가 아닙니다.
그때
iStep:=cStepGetJointsStr;
END_IF;
cStepGetJointsStr:
IF OPCUA_VirtualClient_RoboDK_Station.getJointsStr.bDone
그리고 OPCUA_VirtualClient_RoboDK_Station.getJointsStr.bBusy가 아닙니다.
그때
iStep:=cStepGetJointsStrReset;
ELSIF OPCUA_VirtualClient_RoboDK_Station.getJointsStr.bError THEN
iStep:=cStepGetJointsStrError;
END_IF
cStepGetJointsStrReset:
OPCUA_VirtualClient_RoboDK_Station.getJointsStr.bError가 아닌 경우
그리고 OPCUA_VirtualClient_RoboDK_Station.getJointsStr.bBusy가 아닙니다.
그때
iStep:=cStepSetJointStrDelay;
END_IF;
cStepSetJointStrDelay:
strJointsCommand:=''; strJointsCommand:=CONCAT(LREAL_TO_STRING(arrJointsCommand[1]),strJointsCommand);
strJointsCommand:=CONCAT(strJointsCommand,',');
strJointsCommand:=CONCAT(strJointsCommand,LREAL_TO_STRING(arrJointsCommand[2]));
strJointsCommand:=CONCAT(strJointsCommand,',');
strJointsCommand:=CONCAT(strJointsCommand,LREAL_TO_STRING(arrJointsCommand[3]));
strJointsCommand:=CONCAT(strJointsCommand,',');
strJointsCommand:=CONCAT(strJointsCommand,LREAL_TO_STRING(arrJointsCommand[4]));
strJointsCommand:=CONCAT(strJointsCommand,',');
strJointsCommand:=CONCAT(strJointsCommand,LREAL_TO_STRING(arrJointsCommand[5]));
strJointsCommand:=CONCAT(strJointsCommand,',');
strJointsCommand:=CONCAT(strJointsCommand,LREAL_TO_STRING(arrJointsCommand[6]));
TON2(IN:=TRUE,PT:=T#0.2S);
IF TON2.Q THEN
ton2(in:=false);
iStep:=cStepSetJointsStr;
END_IF
cStepSetJointsStr:
IF (
OPCUA_VirtualClient_RoboDK_Station.setJointsStr.bDone
그리고
OPCUA_VirtualClient_RoboDK_Station.setJointsStr.bBusy
)
또는 bWrite
그때
iStep:=cStepSetJointsStrReset;
ELSIF OPCUA_VirtualClient_RoboDK_Station.setJointsStr.bError
그때
iStep:=cStepSetJointsStrError;
END_IF
cStepSetJointsStrReset:
bWrite:=FALSE;
OPCUA_VirtualClient_RoboDK_Station.setJointsStr.bExecute:=FALSE;
IF NOT OPCUA_VirtualClient_RoboDK_Station.setJointsStr.bError
그리고 OPCUA_VirtualClient_RoboDK_Station.setJointsStr.bBusy가 아닙니다.
그때
아이스텝:=c스텝엔드;
END_IF;
cStepEnd:
TON(IN:=TRUE,PT:=T#0.1S);
IF TON.Q THEN
ton(in:=false);
디버그하지 않으면
iStep:=10;
기타
iStep:=cStepSetJointStrDelay;
END_IF;
END_IF
cStepGetItemError:
Item_ID:=0;
iStep:=cStepWaitReset;
cStepGetJointsError:
FOR i :=0 TO 11 DO
arrJoints[i]:=-99999.99;
END_FOR
iStep:=cStepWaitReset;
cStepGetJointsStrError:
strJoints:='';
iStep:=cStepWaitReset;
cStepWaitReset:
IF bReset THEN
iStep:=cStepInit;
bReset:=FALSE;
END_IF;
END_CASE
aSplit[1] := strJoints;
FOR i:=1 TO 7 DO
bResultSplit := FindAndSplit(
pSeparator := ADR(sSeparator)
,pSrcString := ADR(aSplit[i])
,pLeftString:= ADR(aSplit[i])
,nLeftSize := SIZEOF(aSplit[i])
,pRightString:= ADR(aSplit[i+1])
,nRightSize := SIZEOF(aSplit[i+1])
,bSearchFromRight := FALSE);
IF NOT bResultSplit THEN
EXIT;
END_IF
END_FOR
FOR i :=1 TO 6 DO
arrJointsFromStr[i]:=STRING_TO_LREAL(aSplit[i]);
END_FOR;
//
OPCUA_VirtualClient_RoboDK_Station.getItem(
bExecute:=iStep=cStepGetItem
,Item_Name:=로봇_이름
);
OPCUA_VirtualClient_RoboDK_Station.getJoints(
bExecute:=iStep=cStepGetJoints
,Item_ID:=Item_ID,Joints=>arrJoints
);
OPCUA_VirtualClient_RoboDK_Station.getJointsStr(
bExecute:=iStep=cStepGetJointsStr
,로봇_이름:=로봇_이름,조인트=>strJoints
);
IF bWrite THEN
OPCUA_VirtualClient_RoboDK_Station.setJointsStr(
bExecute:=TRUE
,로봇_이름:=로봇_이름,조인트:=strJointsCommand);
END_IF;