在這篇教學中你會知道如何在RoboDK中起動OPC UA 伺服器,然後使用UaExpert軟件和Beckhoff TwinCAT3 TF6100來測試其功能。
你可以利用在OPC UA插件去設定自己的OPC UA連接設定,例如接口號碼或IP地址,,伺服器的使用不使用等等。當RoboDK 專案OPC UA插件被激活後,你可以從OPC UA-OPC-UA Settings執行這些相關設定。
如圖所示,OPC UA的設定畫面會表示在你的RoboDK軟件左方。
提示:你可以根據自己的應用來修改OPC UA 伺服器的接口號碼/ IP地址,然後點擊”start”去起動你的OPC UA 伺服器。
假若你看到“RoboDK’s OPC UA server running on port 4840”的信息,那就代表你的OPC UA 伺服器已經起動成功。
你可以在任何的RoboDK工作站上測試一台或複數的機械人的OPC UA 連接性能。
你可以使用免費軟UaExpert來測試RoboDK OPC UA伺服器的性能。請點擊以下連結下載安裝檔案。
https://www.unified-automation.com/downloads/opc-ua-clients.html.
執行UaExpert軟件,點擊+按鈕去新增OPC UA伺服器。
展開Custom Discovery菜單,雙擊<Double clicks to Add Server.>來新增伺服器。
輸入你的OPC UA伺服器地址。
選擇None作為安全連接選項,點擊OK開始連接OPC UA伺服器。
你在UaExpert新增了OPC UA伺服器。
現在你可以利用UaExpert嘗試連接OPC UA伺服器。
如果連接成功,你會看到OPC UA伺服器內的所有Node 和Method。
RoboDK OPC UA伺服器提供不同的node讓其他裝置可以讀寫RoboDK工作站的情報。
你可以使用RoboDK node來取得RoboDK的軟件版本。
如圖所示,我們在這篇教學中使用了RoboDK 64 Bit v5.5.3.23031。
你可以使用Simulation Speed node來讀寫RoboDK工作站的仿真速度。
SimulationSpeed的現在值是和RoboDK工作站上的仿真速度設定是一樣,而且你可以直接更改SimulationSpeed node的數值來覆寫這個設定。
你可以使用Station Node來讀取RoboDK工作站的名稱。
如圖所示,我們在這篇教學中使用了RoboDK工作站名稱是”New Station(1)”。
你可以使用Station Parameter 和Station Value這對Node來讀寫RoboDK工作站的參數。RoboDK OPC UA伺服器會重覆監視Station Parameter,然後根據其現在值去讀取工作站參數的現在值,最後將其值傳送到Station Value Node。
你可以右擊view >點擊Station parameters來確認自己工作站的參數。
工作站所有默認的參數和其參數值都表示在Constant parameters的表格內。
Stationparameter是等於Parameter,而StationValue是相等於Value。
你也可以點擊“Add“來新增屬於自己工作站的參數。
如圖所示,你的工作站新增了一個參數。
輸入參數的名稱和數值,然後點擊Apply保存。
然後你可以利用StationParameter和StationValue來讀取這個參數。
你可以使用time node來讀取RoboDK工作站的現在時間。
工作站的現在時間會以DataTime格式的儲存下來。
然後這個Node是不停更新的。
RoboDKOPC UA伺服器也提供了一系列的methods讓使用者讀寫RoboDK OPC UA伺服器的情報。我們可以右擊Method>點擊Call來執行Method。
你可以使用getItem來讀取RoboDK工作站內物件的指標。
getItem需要Device Name作為InputArguments,你可以想像, Device Name是任何被配置在RoboDK工作站的其物件名稱,然後OutputArguments就會輸出指定物件的指標。
我們會在Item Name輸入“ABB_RB1”作為InputArguments,然後你會取得工作站中ABB 機械人的指標。
如果你輸入了無效Item Name,Method 的執行結果會是0。
getJonits會根據Item ID來讀取其工作站內機械人的關節情報(配列格式)。
Item ID是工作站物件的指標值,你可以使用剛才所介紹的getItem()來讀取其現在值。
所以我們要先從getItem()中取得工作站內的“ABB_RB1”指標值。
然後你只需要將那個指標值剪報到getJoints method的Item ID上,然後執行method;你就會取得機械人關節的現在值。(配列格式)
getJonits會根據Item ID來讀取其工作站內機械人的關節情報(文字格式)。
利用這個method時,我們不再需要工作站物件的指標,取而代之的是工作站其物件的名稱(文字格式)。
我們現在嘗試利用getJonits method來讀取ABB_RB1現時的關節值。
執行method前,我們只需然在Robot name參數中輸入“ABB_RB1”,其機械人現時的關節值就會自動返回。
我們可以使用setJointsStr method來設定機械人的關節位置(文字列格式)。
在Robot name參數中輸入ABB_RB1然後在Joints參數中輸入你想設定的關節位置,然後執行。
例子:-0.000000,0.000000,-0.000000,-0.000000,-0.0,-0.000000
新增TwincCAT3專案,然後選擇I/O>右擊>點下Add New Item。
從OPC菜單中選擇Virtual OPC UA Device然後點擊OK。
OPC UA Virtual 被新增在你的專案中。
現在我們需要在TwinCAT專案中新加RoboDK OPC UA 伺服器。選擇Device 1 >右擊>點擊Add New Item。
從菜單中選擇OPC UA Client(Module)然後OK。
你的專案中追加了OPC UA 客戶介面。
打開OPC UA 客戶介面然後點擊“Select Endpoint”去設定你要讀取的OPC UA伺服器。
輸入你的OPC UA伺服器地址然後點擊Update。
點擊 “Add Nodes” 你就可以用瀏覽其OPC 伺服器的Node和Method。
如果TwinCAT和OPC 伺服器是已經連接,你就可以瀏覽到所有的情報。
選擇所有的Methods然後按下OK。
RoboDK OPC 伺服器中所有的method都追加了在你的專案中。
設定你自動生成程式的預設名稱。
點擊“Create Plc Code”。
OpcUaClient檔案夾會被新增到你的PLC專案中,然後你會找到所有的RoboDK Method會被自動轉換成IEC61131-3 功能塊格式。
.以下是利用剛才自動生成的功能塊編寫PLC程式來讀寫RoboDK OPC UA 伺服器的程式例子。
PROGRAM MAIN
VAR
bConnected :BOOL;
StationPointer :DINT;
iStep :INT;
bStart :BOOL;;
i :INT;
TON :TON;
bReset :BOOL;
bWrite :BOOL;
TON2 :TON;
bShow :BOOL:=TRUE;
bVis :BOOL:=True;
END_VAR
VAR
Robot_name :STRING(80):='ABB_RB1';
Item_ID :ULINT;
arrJoints :ARRAY[0..11]OF LREAL;
strJoints :STRING(80):='';
arrJointsFromStr:ARRAY[1..11]OF LREAL;
sSeparator :STRING(1) := ',';
arrJointsCommand:ARRAY[1..11]OF LREAL;
strJointsCommand:STRING(80);
END_VAR
VAR CONSTANT
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;
CASE iStep OF
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
IF NOT OPCUA_VirtualClient_RoboDK_Station.getItem.bBusy
AND NOT OPCUA_VirtualClient_RoboDK_Station.getItem.bError
AND NOT OPCUA_VirtualClient_RoboDK_Station.getJoints.bBusy
AND NOT OPCUA_VirtualClient_RoboDK_Station.getJoints.bError
AND NOT OPCUA_VirtualClient_RoboDK_Station.getJointsStr.bBusy
AND NOT OPCUA_VirtualClient_RoboDK_Station.getJointsStr.bError
AND NOT OPCUA_VirtualClient_RoboDK_Station.setJoints.bBusy
AND NOT OPCUA_VirtualClient_RoboDK_Station.setJoints.bError
AND NOT OPCUA_VirtualClient_RoboDK_Station.setJointsStr.bBusy
AND NOT OPCUA_VirtualClient_RoboDK_Station.setJointsStr.bError
THEN
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:
IF NOT OPCUA_VirtualClient_RoboDK_Station.getItem.bError
AND NOT OPCUA_VirtualClient_RoboDK_Station.getItem.bBusy
THEN
iStep:=cStepGetJoints;
END_IF
cStepGetJoints:
IF OPCUA_VirtualClient_RoboDK_Station.getJoints.bDone
AND NOT OPCUA_VirtualClient_RoboDK_Station.getJoints.bBusy
THEN
iStep:=cStepGetJointsReset;
ELSIF OPCUA_VirtualClient_RoboDK_Station.getJoints.bError THEN
iStep:=991;
END_IF
cStepGetJointsReset:
IF NOT OPCUA_VirtualClient_RoboDK_Station.getItem.bError
AND NOT OPCUA_VirtualClient_RoboDK_Station.getItem.bBusy
THEN
iStep:=cStepGetJointsStr;
END_IF;
cStepGetJointsStr:
IF OPCUA_VirtualClient_RoboDK_Station.getJointsStr.bDone
AND NOT OPCUA_VirtualClient_RoboDK_Station.getJointsStr.bBusy
THEN
iStep:=cStepGetJointsStrReset;
ELSIF OPCUA_VirtualClient_RoboDK_Station.getJointsStr.bError THEN
iStep:=cStepGetJointsStrError;
END_IF
cStepGetJointsStrReset:
IF NOT OPCUA_VirtualClient_RoboDK_Station.getJointsStr.bError
AND NOT OPCUA_VirtualClient_RoboDK_Station.getJointsStr.bBusy
THEN
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
AND NOT
OPCUA_VirtualClient_RoboDK_Station.setJointsStr.bBusy
)
OR NOT bWrite
THEN
iStep:=cStepSetJointsStrReset;
ELSIF OPCUA_VirtualClient_RoboDK_Station.setJointsStr.bError
THEN
iStep:=cStepSetJointsStrError;
END_IF
cStepSetJointsStrReset:
bWrite:=FALSE;
OPCUA_VirtualClient_RoboDK_Station.setJointsStr.bExecute:=FALSE;
IF NOT OPCUA_VirtualClient_RoboDK_Station.setJointsStr.bError
AND NOT OPCUA_VirtualClient_RoboDK_Station.setJointsStr.bBusy
THEN
iStep:=cStepEnd;
END_IF;
cStepEnd:
TON(IN:=TRUE,PT:=T#0.1S);
IF TON.Q THEN
TON(IN:=FALSE);
IF NOT debug THEN
iStep:=10;
ELSE
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:=Robot_name
);
OPCUA_VirtualClient_RoboDK_Station.getJoints(
bExecute:=iStep=cStepGetJoints
,Item_ID:=Item_ID,Joints=>arrJoints
);
OPCUA_VirtualClient_RoboDK_Station.getJointsStr(
bExecute:=iStep=cStepGetJointsStr
,Robot_name:=Robot_name,Joints=>strJoints
);
IF bWrite THEN
OPCUA_VirtualClient_RoboDK_Station.setJointsStr(
bExecute:=TRUE
,Robot_name:=Robot_name,Joints:=strJointsCommand);
END_IF;