This time, I would like to introduce how to connect OPC UA Server via PLC OPEN library, read/write Node, and call Method. First, here is a list of what Function Blocks are required to use each function.
If you are familiar with how to use this PLC OPEN library,the theory is same as other plc , such as Siemens, let’s learn it .
Support DataType
Add the library
As I mentioned earlier, this time we will use the PLC Open function block to connect and exchange data with the OPC UA server, so let’s add the library first.
PLC>Your Project>Reference>Add library.
Search for PLCOPEN_OPCUA and you will find Tc3_PLCopen_OpcUA,3.x.x.x.Select the library and click OK.
Connect/Disconnenct
Let’s start with the simplest functions, Connect and Disconnect. The Function Blocks to use are UA_Connect and UA_Disconnect。
UA_Connect
VAR_INPUT | ||
Execute | BOOL | Execute with a positive edge |
ServerUrl | STRING(MAX_STRING_LENGTH) | OPC UA Server URL |
SessionConnectInfo | ST_UASessionConnectInfo | connection configuration data |
Timeout | TIME | time out setting |
VAR_OUTPUT | ||
ConnectionHdl | DWORD | OPCUA Connection HandleOther Blocks are also used with important variables. |
Done | BOOL | Function Block is executed without error |
Busy | BOOL | Function Block is executing |
Error | BOOL | Error is occupied |
ErrorID | DWORD | The Error ID |
UA_Disconnect
VAR_INPUT | ||
Execute | BOOL | Execute with a positive edge |
ConnectionHdl | DWORD | OPCUA Connection Handle |
Timeout | TIME | Disconnect Timeout設定 |
VAR_OUTPUT | ||
Done | BOOL | Function Block is executed without error |
Busy | BOOL | Function Block is executing |
Error | BOOL | Error is occupied |
ErrorID | DWORD | The Error ID |
Shouldn’t we just proceed to the program analysis of Node value reading at once? Some people may think that, but I believe that everything starts with simplicity. And if you slowly understand the theories and principles behind it, this process will surely come in handy when you do something else “new”.
So, Here is the flow diagram below:
- Using UA_Connect to establish the connection with OPC UA Server
- Using Some Function Block to exchange the data with OPC UA Server
- Using UA_Dissconnect to disconnect the connection with OPC UA Server
Let’s clear Connect and Disconnect first.
And when using UA_Connect, the OPC UA Server first needs those two important pieces of information.
- Server URL
- Session Connect Information
Server URL
Where can I find the Server URL? I think so. I may get information from the customer, and I’m using the same OPC UA Server Demo as last time, so
The URL will be opc.tcp://DESKTOP-7FE1JP2:48010.
Session Connect Information
Session Connect Information was also introduced in the Function Blocks of UA_Connect introduced earlier.
ST_UASessionConnectInfo | ||
sApplicationUri | STRING(MAX_STRING_LENGTH) | Application URL(<=255 characters) |
sApplicationName | STRING(MAX_STRING_LENGTH) | application name(<=255 characters) |
eSecurityMode | E_UASecurityMsgMode | Security Mode setting |
eSecurityPolicyUri | E_UASecurityPolicy | Security policy setting |
eTransportProfileUri | E_UATransportProfile | Transport Profile settings |
tSessionTimeout | TIME | Session Timeout time |
tConnectTimeout | TIME | Connection timeout time |
E_UASecurityMsgMode
- eUASecurityMsgMode_BestAvailable := 0,
- eUASecurityMsgMode_None := 1,
- eUASecurityMsgMode_Sign := 2,
- eUASecurityMsgMode_Sign_Encrypt := 3;
E_UASecurityPolicy
- eUASecurityPolicy_BestAvailable := 0
- eUASecurityPolicy_None := 1,
- eUASecurityPolicy_Basic128 := 2,
- eUASecurityPolicy_Basic128Rsa15 := 3,
- eUASecurityPolicy_Basic256 := 4
E_UATransportProfile
- eUATransportProfileUri_UATcp := 1,
- eUATransportProfileUri_WSHttpBinary := 2,
- eUATransportProfileUri_WSHttpXmlOrBinary := 3,
- eUATransportProfileUri_WSHttpXml := 4
Code
That’s it for the description. Let’s start writing the program and here is the flow:
VAR
Instances for UA_CONNECT and UA_Disconnect are defined in the VAR Area.
I also mentioned earlier that UASessionInfo is a variable required to use the UA_Connect Block.And iState is the variable that manages the connection>do something>disconnect flow.There are also bError and others that indicate the Block execution status.
The array Timer is used as a delay from Disconnect to reconnect.
VAR UA_CONNENT :UA_CONNECT; UASessionInfo :ST_UASessionConnectInfo; UA_Disconnect :UA_Disconnect; iState :INT; bError :BOOL; bDone :BOOL; bBusy :BOOL; bReset :BOOL; nErrorID :DWORD; nConnectionHdi :DWORD; arrTON :ARRAY[0..3]OF TON; END_VAR |
VAR CONSTANT
This is a constant of the iState Step number, Timeout, and OPCUA Server URL.
VAR CONSTANT ciStep_Parameters_Initlize :INT :=0; ciStep_UAConnect :INT :=10; ciStep_UADIsconnect :INT :=20; ciStep_End :INT :=7000; ciStep_UAError :INT :=9000; ctUASession_Connect_Timeout :TIME :=T#1M; ctUASession_Session_Timeout :TIME :=T#1M; ctUAConnect_Timeout :TIME :=T#5S; cstServerURL :STRING :=’opc.tcp://DESKTOP-7FE1JP2:48010′; END_VAR |
PROGRAM
The program is the same as the Flow Chart.
CASE iState OF ciStep_Parameters_Initlize: bError:=FALSE; bDone:=FALSE; bBusy:=FALSE; nErrorID:=0; UASessionInfo.tConnectTimeout:=ctUASession_Connect_Timeout; UASessionInfo.tSessionTimeout:=ctUASession_Session_Timeout; UASessionInfo.eSecurityMode:=eUASecurityMsgMode_None; UASessionInfo.eSecurityPolicyUri:=eUASecurityPolicy_None; UASessionInfo.eTransportProfileUri:=eUATransportProfileUri_UATcp; iState:=ciStep_UAConnect; ciStep_UAConnect: UA_CONNENT( Execute:=TRUE ,ServerUrl:=cstServerURL ,SessionConnectInfo:=UASessionInfo ,Timeout:=ctUAConnect_Timeout ,ConnectionHdl=>nConnectionHdi ); IF UA_CONNENT.Done AND NOT UA_CONNENT.Error THEN iState:=ciStep_UADIsconnect; UA_CONNENT( Execute:=FALSE ); ELSIF UA_CONNENT.Error THEN iState:=ciStep_UAError; nErrorID:=UA_CONNENT.ErrorID; END_IF |
ciStep_UADIsconnect: UA_Disconnect( Execute:=TRUE ,ConnectionHdl:=nConnectionHdi ); IF UA_Disconnect.Done AND NOT UA_Disconnect.Error THEN iState:=ciStep_End; ELSIF UA_Disconnect.Error THEN iState:=ciStep_UAError; nErrorID:=UA_Disconnect.ErrorID; END_IF ciStep_End: arrTON[0]( IN:=TRUE ,PT:=T#2S ); IF arrton[0].Q THEN arrton[0]( IN:=FALSE ); iState:=ciStep_UAConnect; END_IF ciStep_UAError: bError:=TRUE; IF bReset THEN iState:=ciStep_Parameters_Initlize; END_IF; END_CASE |
Polling (Read)
Now that we do the Polling,and it can reads and writes Nodes from the UA Name space.
In the actual flow, a connection is established with UA_Connect, Namespace is obtained with UA_GetNamespaceIndex > Handle is obtained > Node value is read > Handle is released > Disconnect.
UA_GetNamespaceIndex
VAR_INPUT | ||
Execute | BOOL | Execute with a Positive edge |
ConnectionHdl | DWORD | OPCUA Connection Handle |
NamespaceUri | STRING(MAX_STRING_LENGTH) | Specify NamespaceURL. With TwinCAT Server,urn:BeckhoffAutomation:Ua:PLC1 is used, |
Timeout | TIME | Disconnect Timeout setting |
VAR_OUTPUT | ||
NamespaceIndex | UINT | The Namesapce numbers that will be used in UA_Read,etc. |
Done | BOOL | Function Block is executed without error |
Busy | BOOL | Function Block is executing |
Error | BOOL | Error is occupied |
ErrorID | DWORD | The Error ID |
UA_NodeGetHandle
VAR_INPUT | ||
Execute | BOOL | Execute with a Positive edge |
ConnectionHdl | DWORD | OPCUA Connection Handle |
NodeID | ST_UANodeID | The index that you got from UA_GetNamesapceIndex |
Timeout | TIME | Disconnect Timeout Setting |
VAR_OUTPUT | ||
NamespaceIndex | UINT | The Namesapce numbers that will be used in UA_Read,etc. |
Done | BOOL | Function Block is executed without error |
Busy | BOOL | Function Block is executing |
Error | BOOL | Error is occupied |
ErrorID | DWORD | The Error ID |
UA_Read
VAR_INPUT | ||
Execute | BOOL | Execute with a Positive edge |
ConnectionHdl | DWORD | OPCUA Connection Handle |
NodeHdl | ST_UANodeID | The index that you got from UA_GetNamesapceIndex |
stNodeAddInfo | ST_UANodeAdditionalInfo | The node settings |
pVariable | PVOID | Memory Pointer |
cbData | UDINT | The size of node that you need to read(bytes) |
Timeout | TIME | Timeout setting |
VAR_OUTPUT | ||
Done | BOOL | Function Block is executed without error |
Busy | BOOL | Function Block is executing |
Error | BOOL | Error is occupied |
ErrorID | DWORD | The Error ID |
cbData_R | UDINT | Total Bytes that is read |
UA_NodeReleaseHandle
VAR_INPUT | ||
Execute | BOOL | Execute with a Positive edge |
ConnectionHdl | DWORD | OPCUA Connection Handle |
NodeHdl | ST_UANodeID | This is the Index obtained from the previous UA_GetNamesapceIndex. |
Timeout | TIME | Timeout setting |
VAR_OUTPUT | ||
Done | BOOL | Function Block is executed without error |
Busy | BOOL | Function Block is executing |
Error | BOOL | Error is occupied |
ErrorID | DWORD | The Error ID |
NamespaceURI
When you looked at the UA_GetNamesapceIndex Function Block, there was a NamespaceUri parameter, right?
If you make a mistake in that, the subsequent Handle and Read Function Blocks will result in execution errors, so I would like to explain here.
Open UAExpert and connect to OPC UA Server and browse Server>NamespaceArray.
Then you will get an array of strings. Note that the Namespace will change depending on the Node you want to access later.
For example I want to access Demo>000_Static>Scalar>Int16.
If you browse this Node, you will see that NamespaceIndex is 2.
So the NamespaceUrl will use the second URL.
Node Identifier
Next, UA_NodeGetHandle has a parameter called NodeID. Its NodeID is defined from the DUT called ST_UANodeID.
ST_UANodeID
- nNamespaceIndex : UINT;
- nReserved : ARRAY [1..2] OF BYTE; //fill bytes
- sIdentifier : STRING(MAX_STRING_LENGTH);
- eIdentifierType : E_UAIdentifierType;
- E_UAIdentifierType
- eUAIdentifierType_String := 1,
- eUAIdentifierType_Numeric := 2,
- eUAIdentifierType_GUID := 3,
- eUAIdentifierType_Opaque := 4
- E_UAIdentifierType
nNamespaceIndex puts the NameIndex obtained from UA_GetNamesapceIndex.
eIdentifierType needs to be defined with String or Number. (IdentifierType in the figure below)For sIdentifier, just copy the Identifier value in the figure below.
Code
Here is the flow.
VAR
VAR //Connect/DisConnect Functionality UA_CONNENT :UA_CONNECT; UASessionInfo :ST_UASessionConnectInfo; UA_Disconnect :UA_Disconnect; //Polling Functionlity //Get NameIndex UA_GetNamespaceIndex:UA_GetNamespaceIndex; nNameSpaceIndex :UINT; //Get Handle UA_NodeGetHandle:UA_NodeGetHandle; NodeID :ST_UANodeID; nNodeHdl :DWORD; //UA Read UA_Read :UA_Read; stIndexRange : ARRAY [1..nMaxIndexRange] OF ST_UAIndexRange; nIndexRangeCount : UINT; stNodeAddInfo : ST_UANodeAdditionalInfo; nReadData : INT; cbDataRead : UDINT; //UA Node Release Handle UA_NodeReleaseHandle : UA_NodeReleaseHandle; iState :INT; bError :BOOL; bDone :BOOL; bBusy :BOOL; bReset :BOOL; bInit :BOOL; nErrorID :DWORD; nConnectionHdi :DWORD; arrTON :ARRAY[0..3]OF TON; END_VAR |
VAR_CONSTANT
VAR CONSTANT ciStep_Parameters_Initlize :INT :=0; ciStep_UAConnect :INT :=10; ciStep_UAGetNamespaceIndex :INT :=20; ciStep_UAGetHandle :INT :=30; ciStep_UARead :INT :=40; ciStep_UARelease :INT :=50; ciStep_UADIsconnect :INT :=100; ciStep_End :INT :=7000; ciStep_UAError :INT :=9000; ctUASession_Connect_Timeout :TIME :=T#1M; ctUASession_Session_Timeout :TIME :=T#1M; ctUAConnect_Timeout :TIME :=T#5S; cstServerURL :STRING :=’opc.tcp://DESKTOP-7FE1JP2:48010′; END_VAR |
PROGRAM
CASE iState OF ciStep_Parameters_Initlize: bError:=FALSE; bDone:=FALSE; bBusy:=FALSE; nErrorID:=0; UASessionInfo.tConnectTimeout:=ctUASession_Connect_Timeout; UASessionInfo.tSessionTimeout:=ctUASession_Session_Timeout; UASessionInfo.eSecurityMode:=eUASecurityMsgMode_None; UASessionInfo.eSecurityPolicyUri:=eUASecurityPolicy_None; UASessionInfo.eTransportProfileUri:=eUATransportProfileUri_UATcp; stNodeAddInfo.nIndexRangeCount := nIndexRangeCount; stNodeAddInfo.stIndexRange := stIndexRange; UA_CONNENT( Execute:=FALSE ); UA_Disconnect( Execute:=FALSE ); UA_GetNamespaceIndex( Execute:=FALSE ); UA_NodeGetHandle( Execute:=FALSE ); UA_Read( Execute:=FALSE ); UA_NodeReleaseHandle( Execute:=FALSE ); IF NOT UA_CONNENT.Busy AND NOT UA_Disconnect.Busy AND NOT UA_GetNamespaceIndex.Busy AND NOT UA_NodeGetHandle.Busy AND NOT UA_Read.Busy AND NOT UA_NodeReleaseHandle.Busy THEN iState:=ciStep_UAConnect; END_IF; ciStep_UAConnect: UA_CONNENT( Execute:=TRUE ,ServerUrl:=cstServerURL ,SessionConnectInfo:=UASessionInfo ,Timeout:=ctUAConnect_Timeout ,ConnectionHdl=>nConnectionHdi ); IF UA_CONNENT.Done AND NOT UA_CONNENT.Error THEN iState:=ciStep_UAGetNamespaceIndex; UA_CONNENT( Execute:=FALSE ); ELSIF UA_CONNENT.Error THEN iState:=ciStep_UAError; nErrorID:=UA_CONNENT.ErrorID; END_IF ciStep_UAGetNamespaceIndex: UA_GetNamespaceIndex( Execute:=TRUE ,ConnectionHdl:=nConnectionHdi ,NamespaceUri :=’http://www.unifiedautomation.com/DemoServer/’ ,NamespaceIndex=>nNameSpaceIndex ); IF UA_GetNamespaceIndex.Done AND NOT UA_GetNamespaceIndex.Error THEN UA_GetNamespaceIndex( Execute:=FALSE ); iState:=ciStep_UAGetHandle; ELSIF UA_GetNamespaceIndex.Error THEN nErrorID:=UA_GetNamespaceIndex.ErrorID; iState:=ciStep_UAError; END_IF ciStep_UAGetHandle: NodeID.eIdentifierType:=eUAIdentifierType_String; NodeID.nNamespaceIndex:=nNameSpaceIndex; NodeID.sIdentifier:=’Demo.Static.Scalar.Int16′; UA_NodeGetHandle( Execute:=TRUE ,ConnectionHdl:=nConnectionHdi ,NodeID:=NodeID ,NodeHdl=>nNodeHdl ); IF UA_NodeGetHandle.Done AND NOT UA_NodeGetHandle.Error THEN UA_NodeGetHandle( Execute:=FALSE ); iState:=ciStep_UARead; ELSIF UA_NodeGetHandle.Error THEN nErrorID:=UA_NodeGetHandle.ErrorID; iState:=ciStep_UAError; END_IF ciStep_UARead: UA_Read( Execute:=TRUE ,ConnectionHdl:=nConnectionHdi ,NodeHdl:=nNodeHdl ,cbData:=SIZEOF(nReadData) ,stNodeAddInfo:=stNodeAddInfo ,pVariable:=ADR(nReadData) ); IF UA_Read.Done AND NOT UA_Read.Error THEN UA_Read( Execute:=FALSE ,cbData_R=>cbDataRead ); iState:=ciStep_UARelease; ELSIF UA_Read.Error THEN nErrorID:=UA_Read.ErrorID; iState:=ciStep_UAError; END_IF; ciStep_UARelease: UA_NodeReleaseHandle( Execute:=TRUE ,ConnectionHdl:=nConnectionHdi ,NodeHdl:=nNodeHdl ); IF UA_NodeReleaseHandle.Done AND NOT UA_NodeReleaseHandle.Error THEN UA_NodeReleaseHandle( Execute:=FALSE ); iState:=ciStep_End; ELSIF UA_NodeReleaseHandle.Error THEN nErrorID:=UA_NodeReleaseHandle.ErrorID; iState:=ciStep_UAError; END_IF ciStep_UADIsconnect: UA_Disconnect( Execute:=TRUE ,ConnectionHdl:=nConnectionHdi ); IF UA_Disconnect.Done AND NOT UA_Disconnect.Error THEN iState:=ciStep_End; ELSIF UA_Disconnect.Error THEN iState:=ciStep_UAError; nErrorID:=UA_Disconnect.ErrorID; END_IF ciStep_End: arrTON[0]( IN:=TRUE ,PT:=T#2S ); IF arrton[0].Q THEN arrton[0]( IN:=FALSE ); iState:=ciStep_UAConnect; END_IF ciStep_UAError: bError:=TRUE; IF bReset THEN iState:=ciStep_Parameters_Initlize; END_IF; END_CASE |
Result
Currently the Node value is modified as 99.
Similarly, nReadData from UA_Read() is also 99.
Polling(Write)
Now we can operate the write function.
In the same flow as UA_Read, just change the Function Block to UA_Write().
UA_Write
VAR_INPUT | ||
Execute | BOOL | Execute with a positive edge |
ConnectionHdl | DWORD | OPCUA Connection Handle |
NodeHdl | ST_UANodeID | The index that get from UA_GetNamesapceIndex |
stNodeAddInfo | ST_UANodeAdditionalInfo | The Node information |
pVariable | PVOID | Memory Pointer |
cbData | UDINT | The total bytes that will write into the Node. |
Timeout | TIME | Timeout setting |
VAR_OUTPUT | ||
Done | BOOL | Function Block is executed without error |
Busy | BOOL | Function Block is executing |
Error | BOOL | Error is occupied |
ErrorID | DWORD | The Error ID |
cbData_R | UDINT | The total bytes are written in this request |
Code
Here is the Flow.
VAR
VAR //Connect/DisConnect Functionality UA_CONNENT :UA_CONNECT; UASessionInfo :ST_UASessionConnectInfo; UA_Disconnect :UA_Disconnect; //Polling Functionlity //Get NameIndex UA_GetNamespaceIndex:UA_GetNamespaceIndex; nNameSpaceIndex :UINT; //Get Handle UA_NodeGetHandle:UA_NodeGetHandle; NodeID :ST_UANodeID; nNodeHdl :DWORD; //UA Read UA_Read :UA_Read; stIndexRange : ARRAY [1..nMaxIndexRange] OF ST_UAIndexRange; nIndexRangeCount : UINT; stNodeAddInfo : ST_UANodeAdditionalInfo; nReadData : INT; cbDataRead : UDINT; //UA Write UA_Write : UA_Write; nWriteData: INT := 0; //UA Node Release Handle UA_NodeReleaseHandle : UA_NodeReleaseHandle; iState :INT; bError :BOOL; bDone :BOOL; bBusy :BOOL; bReset :BOOL; bInit :BOOL; nErrorID :DWORD; nConnectionHdi :DWORD; arrTON :ARRAY[0..3]OF TON; END_VAR |
VAR_CONSTANT
VAR CONSTANT ciStep_Parameters_Initlize :INT :=0; ciStep_UAConnect :INT :=10; ciStep_UAGetNamespaceIndex :INT :=20; ciStep_UAGetHandle :INT :=30; ciStep_UARead :INT :=40; ciStep_UARelease :INT :=50; ciStep_UAWrite :INT :=60; ciStep_UADIsconnect :INT :=100; ciStep_End :INT :=7000; ciStep_UAError :INT :=9000; ctUASession_Connect_Timeout :TIME :=T#1M; ctUASession_Session_Timeout :TIME :=T#1M; ctUAConnect_Timeout :TIME :=T#5S; cstServerURL :STRING :=’opc.tcp://DESKTOP-7FE1JP2:48010′; END_VAR |
PROGRAM
CASE iState OF ciStep_Parameters_Initlize: bError:=FALSE; bDone:=FALSE; bBusy:=FALSE; nErrorID:=0; UASessionInfo.tConnectTimeout:=ctUASession_Connect_Timeout; UASessionInfo.tSessionTimeout:=ctUASession_Session_Timeout; UASessionInfo.eSecurityMode:=eUASecurityMsgMode_None; UASessionInfo.eSecurityPolicyUri:=eUASecurityPolicy_None; UASessionInfo.eTransportProfileUri:=eUATransportProfileUri_UATcp; stNodeAddInfo.nIndexRangeCount := nIndexRangeCount; stNodeAddInfo.stIndexRange := stIndexRange; UA_CONNENT( Execute:=FALSE ); UA_Disconnect( Execute:=FALSE ); UA_GetNamespaceIndex( Execute:=FALSE ); UA_NodeGetHandle( Execute:=FALSE ); UA_Read( Execute:=FALSE ); UA_NodeReleaseHandle( Execute:=FALSE ); IF NOT UA_CONNENT.Busy AND NOT UA_Disconnect.Busy AND NOT UA_GetNamespaceIndex.Busy AND NOT UA_NodeGetHandle.Busy AND NOT UA_Read.Busy AND NOT UA_NodeReleaseHandle.Busy THEN iState:=ciStep_UAConnect; END_IF; ciStep_UAConnect: UA_CONNENT( Execute:=TRUE ,ServerUrl:=cstServerURL ,SessionConnectInfo:=UASessionInfo ,Timeout:=ctUAConnect_Timeout ,ConnectionHdl=>nConnectionHdi ); IF UA_CONNENT.Done AND NOT UA_CONNENT.Error THEN iState:=ciStep_UAGetNamespaceIndex; UA_CONNENT( Execute:=FALSE ); ELSIF UA_CONNENT.Error THEN iState:=ciStep_UAError; nErrorID:=UA_CONNENT.ErrorID; END_IF ciStep_UAGetNamespaceIndex: UA_GetNamespaceIndex( Execute:=TRUE ,ConnectionHdl:=nConnectionHdi ,NamespaceUri :=’http://www.unifiedautomation.com/DemoServer/’ ,NamespaceIndex=>nNameSpaceIndex ); IF UA_GetNamespaceIndex.Done AND NOT UA_GetNamespaceIndex.Error THEN UA_GetNamespaceIndex( Execute:=FALSE ); iState:=ciStep_UAGetHandle; ELSIF UA_GetNamespaceIndex.Error THEN nErrorID:=UA_GetNamespaceIndex.ErrorID; iState:=ciStep_UAError; END_IF ciStep_UAGetHandle: NodeID.eIdentifierType:=eUAIdentifierType_String; NodeID.nNamespaceIndex:=nNameSpaceIndex; NodeID.sIdentifier:=’Demo.Static.Scalar.Int16′; UA_NodeGetHandle( Execute:=TRUE ,ConnectionHdl:=nConnectionHdi ,NodeID:=NodeID ,NodeHdl=>nNodeHdl ); IF UA_NodeGetHandle.Done AND NOT UA_NodeGetHandle.Error THEN UA_NodeGetHandle( Execute:=FALSE ); iState:=ciStep_UAWrite; ELSIF UA_NodeGetHandle.Error THEN nErrorID:=UA_NodeGetHandle.ErrorID; iState:=ciStep_UAError; END_IF ciStep_UAWrite: nWriteData:=nWriteData+1; UA_Write( Execute:=TRUE ,ConnectionHdl:=nConnectionHdi ,NodeHdl:=nNodeHdl ,stNodeAddInfo:=stNodeAddInfo ,pVariable:=ADR(nWriteData) ,cbData:=SIZEOF(nWriteData) ); IF UA_Write.Done AND NOT UA_Read.Error THEN UA_Write( Execute:=FALSE ,pVariable:=ADR(nWriteData) ); iState:=ciStep_UARelease; ELSIF UA_Write.Error THEN nErrorID:=UA_Write.ErrorID; iState:=ciStep_UAError; END_IF; ciStep_UARelease: UA_NodeReleaseHandle( Execute:=TRUE ,ConnectionHdl:=nConnectionHdi ,NodeHdl:=nNodeHdl ); IF UA_NodeReleaseHandle.Done AND NOT UA_NodeReleaseHandle.Error THEN UA_NodeReleaseHandle( Execute:=FALSE ); iState:=ciStep_End; ELSIF UA_NodeReleaseHandle.Error THEN nErrorID:=UA_NodeReleaseHandle.ErrorID; iState:=ciStep_UAError; END_IF ciStep_UADIsconnect: UA_Disconnect( Execute:=TRUE ,ConnectionHdl:=nConnectionHdi ); IF UA_Disconnect.Done AND NOT UA_Disconnect.Error THEN iState:=ciStep_End; ELSIF UA_Disconnect.Error THEN iState:=ciStep_UAError; nErrorID:=UA_Disconnect.ErrorID; END_IF ciStep_End: arrTON[0]( IN:=TRUE ,PT:=T#2S ); IF arrton[0].Q THEN arrton[0]( IN:=FALSE ); iState:=ciStep_UAConnect; END_IF ciStep_UAError: bError:=TRUE; IF bReset THEN iState:=ciStep_Parameters_Initlize; END_IF; END_CASE |
Result
Please download the source code from this link: