OPC UA Server Example

In this example you’ll learn how to enable the OPC UA addin and convert RoboDK into an OPC UA server. We’ll browse some settings by using UaExpert software and Beckhoff TwinCAT3 TF6100.

OPC UA - Image 4

Configure your OPC UA Server

The OPC UA add-in allows you to configure some settings such as the server port. You can also choose to activate the server, deactivate it or automatically start with RoboDK.

With the OPC UA add-in enabled, select OPC UA-OPC-UA Settings to configure your OPC UA settings.

OPC UA Settings Screen is shown on the Left side as shown in the following image.

OPC UA - Image 5

OPC UA - Image 6

If you see a message such as “RoboDK’s OPC UA server running on port 4840” it means the OPC UA server in RoboDK started.

Create your Own Station

You can test the OPC UA connectivity with any RoboDK station that has one or more robots.

OPC UA - Image 7

Implementation with UaExpert

You can use UaExpert software to test the connectivity with the RoboDK OPC UA Server.

You can download the free version of the UaExpert software from the Unified Automation website: https://www.unified-automation.com/downloads/opc-ua-clients.html.

OPC UA - Image 8

Add Server

Launch the UaExpert and Click the “+” Button to Add the RoboDK OPC UA Server.

OPC UA - Image 9

Expand the Custom Discovery and select the <Double clicks to Add Server.> option to add the RoboDK OPC UA Server.

OPC UA - Image 10

Enter the URL of the OPC UA server, opc.tcp://127.0.0.1:48440 which you configured in the previous step. 

OPC UA - Image 11

Connect the OPC UA Server with “None” Security.

OPC UA - Image 12

Server is configured.

OPC UA - Image 13

Connect to the Server

Now you can connect to the RoboDK OPC UA Server from UaExpert.

OPC UA - Image 14

You can see the Nodes and Methods when the connection is established.

OPC UA - Image 15

Nodes

There are some nodes inside the RoboDK OPC UA server to let you exchange some basic information about your station.

RoboDK

RoboDK node is a Node that provides the Actual Version of your RoboDK Software.

OPC UA - Image 16

The version RoboDK 64 Bit v5.5.3.23031 was used in this example.

OPC UA - Image 17

SimulationSpeed

Simulation Speed is a node that shows the actual Simulation Speed and allows the user to overwrite the current Simulation Speed.

OPC UA - Image 18

The node value is referenced to the Slide bar of simulation speed.

The current Simulation can be read from this node and can overwrite the simulation speed.

OPC UA - Image 19

Station

Station Node is a node that allows the user to get the current name of the Station in RoboDK.

OPC UA - Image 20

As you see below, the Station node is referenced to your “Station Name” in RoboDK.

OPC UA - Image 21

Station parameters/Station Value

Station Parameter and Station Value are a pair set Node that allows the user to get or set any parameters inside your Station. The RoboDK OPC UA Server will continuously monitor the actual value of “StationParameter” and return the Value of that “StationParameter”, from the Station Value Node.

OPC UA - Image 22

You can view your Station parameters by Right Click your RoboDK Station>Station parameters.

OPC UA - Image 23

In the Constant parameters field, you can see the default station parameters and their value.

OPC UA - Image 24

Station parameter is referenced to the “Parameter” field and Station Value is referenced to the “Value” field.

OPC UA - Image 25

And we can create our own Parameters by clicking the “Add” Button.

OPC UA - Image 26

A new Station parameter is added.

OPC UA - Image 27

Enter your Parameter name and the Parameter Value, then press Apply to save it.

OPC UA - Image 28

You can get your own station parameter as well.

OPC UA - Image 29

Time

The node time is a node that allows you to get the current time of the RoboDK Station.

OPC UA - Image 30

A value with DataTime format is returned.

OPC UA - Image 31

And this Node is updated continually.

OPC UA - Image 32

Methods

RoboDK OPC UA Server is also provided with some methods to allow the user to access the RoboDK station ‘s Data dynamically.

We can just right click the Method>Call to execute the method.

OPC UA - Image 33

getItem

getItem is a Method that allows the user to get the pointer of your Item.

OPC UA - Image 34

For the InputArguments, Device Name is required, you can image the Device Name is your Station Name,Robot Name..etc.. And Item ID is the OutputArguments that return the Pointer of that Device.

OPC UA - Image 35

In this Example, I received the Item ID (Pointer) of my ABB Robot that is named as “ABB_RB1”.

OPC UA - Image 36

0 is returned if the Item Name is invalid or does not exist inside your station.

OPC UA - Image 37

getJoints

getJonits is a method that allows the user to get the joint value of the robot from the station, based on the Item ID.

OPC UA - Image 38

The Item ID is the pointer value of your Item, and you can get it from getItem() Method.

OPC UA - Image 39

We will get the Item ID with this “ABB_RB1” Item name, and a UInt64 value is returned.

OPC UA - Image 40

Joints value is returned while passing the Item ID in the method that we got in the previous.

OPC UA - Image 41

getJointsStr

getJointsStr is a method that allows the user to get the Joints value based on a String Value.

OPC UA - Image 42

We can pass the Robot name (String) in this method.

OPC UA - Image 43

In My Station, ABB_RB1 is my robot’s name.

OPC UA - Image 44

We can just pass “ABB_RB1” in the Robot name parameter and call the method - The joint value in String format is returned.

OPC UA - Image 45

setJointsStr

setJointsStr is a method that allows the user to set the Joints value of the Robot, based on a String Value.

OPC UA - Image 46

In the Robot name, ABB_RB1 is passed, and we can just pass a string with the joint value in the Joints parameter.

For example:-0.000000,0.000000,-0.000000,-0.000000,-0.0,-0.000000

OPC UA - Image 47

Implementation with Beckhoff TwinCAT3

Add Server

Now we can insert the OPC UA Client by I/O>Devices>Add New Item.

OPC UA - Image 48

Select Virtual OPC UA Device from OPC >OK.

OPC UA - Image 49

OPC UA Virtual is inserted.

OPC UA - Image 50

We need to add an OPC UA Client to access the RoboDK OPC UA Server.

Select Device 1 >Right Click >Add New Item.

OPC UA - Image 51

Select “OPC UA Client(Module)” and Ok.

OPC UA - Image 52

OPC UA Client is inserted.

OPC UA - Image 53

Configure the Server

Open the OPC UA Client >Go to Settings Tab>click the “Select Endpoint” to configure the OPC UA Server endpoint that you would like to access.

OPC UA - Image 54

Enter the RoboDK OPC UA server URL and Update it.

OPC UA - Image 55

Add RoboDK Server Method

Press “Add Nodes” to browse the node that is inside the OPC UA Server.

OPC UA - Image 56

If the connection between TwinCAT and OPC UA Server is established, you can Browse the details of OPC UA server.

OPC UA - Image 57

Select all Methods and Ok.

OPC UA - Image 58

Methods are inserted in your Configuration.

OPC UA - Image 59

Auto Generate RoboDK Method

Configure your Name Prefix in this field.

OPC UA - Image 60

Press “Create Plc Code” to create the PLC Code from TwinCAT.

OPC UA - Image 61

An OpcUaClient folder is created in your project, and all RoboDK Method are created in IEC61131-3 Function Block format.

OPC UA - Image 62

PLC Program Example

This section shows a sample program of a Beckhoff TwinCAT PLC that communicates with RoboDK OPC UA server.

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;