PLCopen-Funktionsbausteine
Der TwinCAT OPC UA Client bietet mehrere Möglichkeiten, direkt aus der Steuerungslogik heraus mit einem oder mehreren OPC UA Servern zu kommunizieren. Zum einen gibt es ein TwinCAT-I/O-Gerät, welches eine eine einfache, Mapping-basierte Schnittstelle bietet. Zum anderen stehen durch die PLCopen genormte Funktionsbausteine zur Verfügung, über die eine Verbindung mit einem OPC UA Server direkt aus der SPS-Logik heraus initiiert werden kann. Die Handhabung dieser Bausteine sollen im Folgenden näher beschrieben werden. Dieser Artikel besteht aus den folgenden Sektionen:
- Workflow
- Bestimmung der Kommunikatonsparameter
- Herstellen einer Verbindung
- Auslesen von Variablen
- Schreiben von Variablen
- Aufruf von Methoden
Workflow
Der allgemeine Workflow bei der Verwendung der PLCopen-Funktionsbausteine lässt sich wie folgt schematisch darstellen:

In der Vorbereitungsphase werden die Kommunikationsparameter eingerichtet und eine Verbindung zum Server aufgebaut. Anschliessend erfolgt die Ausführung der gewünschten Funktion (Lesen, Schreiben, Methodenaufrufe), gefolgt vom Trennen der Kommunikationsverbindung.
Bestimmung der Kommunikationsparameter
Im Allgemeinen wird ein grafischen OPC UA Client verwendet, um die Attribute eines Knotens oder Methoden zu bestimmen, die zusammen mit den SPS-Funktionsbausteinen verwendet werden müssen, z. B.:
- NodeID
- NamespaceIndex und entsprechender NamespaceURI
- DataType
- MethodNodeID und ObjectNodeID
Die folgende Dokumentation verwendet den generischen OPC UA Client UA Expert als Beispiel. Dieser Client kann über die Webseite von Unified Automation erworben werden: www.unified-automation.com.
Knoten sind durch die folgenden drei Attribute gekennzeichnet, welche die sogenannte NodeID bilden:
- NamespaceIndex: Namensraum, in dem sich der Knoten befindet, z. B. die SPS-Laufzeit
- Identifier: Eindeutiger Bezeichner des Knotens innerhalb seines Namensraums
- IdentifierType: Typ des Knotens: String, Guid und Numeric
Diese Attribute stellen die sogenannte NodeID dar – die Darstellung eines Knotens auf einem OPC UA Server – und werden von vielen nachfolgenden Funktionsbausteinen benötigt.
Mithilfe der Software UA Expert können Sie die Attribute eines Knotens einfach bestimmen, indem Sie eine Verbindung zum OPC UA Server aufbauen und zum gewünschten Knoten browsen. Die Attribute sind dann im Attributes-Panel sichtbar, zum Beispiel:

Nach der OPC‑UA‑Spezifikation kann der NamespaceIndex ein dynamisch generierter Wert sein. Daher müssen OPC UA Clients immer den entsprechenden NamespaceURI zur Auflösung des NamespaceIndex verwenden, bevor ein Knotenhandle erfasst wird.
Um den NamespaceIndex für einen NamespaceURI zu erfassen, verwenden Sie den Funktionsbaustein UA_GetNamespaceIndex. Den hierfür notwendigen NamespaceURI können Sie mithilfe von UA Expert bestimmen, indem Sie eine Verbindung zum OPC UA Server aufbauen und zum Knoten NamespaceArray browsen.

Dieser Knoten enthält Informationen über alle eingetragenen Namespaces auf dem OPC UA Server.
Die entsprechenden NamespaceURIs sind im Attributes-Panel sichtbar, zum Beispiel:

Im obigen Abschnitt wird beispielhaft eine NodeID gezeigt, in der der NamespaceIndex 5 ist. Nach dem in der Abbildung gezeigten NamespaceArray ist der entsprechende NamespaceURI urn://SVENG-NB04/BeckhoffAutomation/Ua/PLC1. Dieser URI kann nun für den Funktionsbaustein UA_GetNamespaceIndex verwendet werden. Der OPC UA Server stellt sicher, dass der URI immer derselbe bleibt, auch nach einem Neustart.
![]() | Korrekten NamespaceIndex beachten Da sich der gezeigte NamespaceIndex verändern kann, sollten für die spätere Nutzung mit anderen Funktionsbausteinen, z. B. UA_Read, UA_Write, zur Auflösung des korrekten NamespaceIndex immer der NamespaceURI in Kombination mit dem Funktionsbaustein UA_GetNamespaceIndex verwendet werden. |
DataType
Der Datentyp eines Knotens ist erforderlich, um zu sehen, welcher SPS-Datentyp verwendet werden muss, um einen ausgelesenen Wert zuzuordnen oder um in einen Knoten zu schreiben. Mithilfe von UA Expert können Sie den Datentyp eine Knotens einfach bestimmen, indem Sie eine Verbindung zum OPC UA Server aufbauen und zum gewünschten Knoten browsen.
Der Datentyp ist dann im Attributes-Panel sichtbar, zum Beispiel:

In diesem Fall ist der Datentyp (DataType) „Int16“. Dieser muss einem äquivalenten Datentyp in der SPS zugeordnet werden, z. B. „INT“.
MethodNodeID und ObjectNodeID
Beim Aufruf von Methoden aus dem OPC‑UA‑Namensraum sind zwei Identifier erforderlich, wenn der Methodenhandle unter Verwendung des Funktionsbausteins UA_MethodGetHandle erfasst wird:
- ObjectNodeID: Identifiziert das UA-Objekt, das die Methode enthält.
- MethodNodeID: Identifiziert die Methode selbst.
Mithilfe von UA Expert können Sie beide NodeIDs einfach bestimmen, indem Sie eine Verbindung zum OPC UA Server aufbauen und zu der gewünschten Methode bzw. dem gewünschten UA-Objekt, das die Methode enthält, browsen.
Beispiel Methode M_Mul:

Der Method Identifier ist dann im Attributes-Panel sichtbar.

Beispiel Objekt fbMathematics:

Der Object Identifier ist dann im Attributes-Panel sichtbar.

Herstellen einer Verbindung
Im nachfolgenden Abschnitt wird beschrieben, wie Sie die Funktionsbausteine TcX_PLCopen_OpcUa verwenden, um eine Verbindung zu einem lokalen oder remote OPC UA Server herzustellen. Diese Verbindung kann dann verwendet werden, um weitere Funktionalitäten aufzurufen, z. B. Knoten auslesen oder schreiben, oder Methoden aufrufen.
Die folgenden Funktionsbausteine sind erforderlich, um eine Verbindung zu einem OPC UA Server herzustellen und später die Sitzung zu unterbrechen: UA_Connect, UA_Disconnect.
![]() | Lesen Sie zunächst den Abschnitt Wie Kommunikationsparameter zu bestimmen sind, um bestimmte UA-Funktionalitäten besser verstehen zu können (z. B., wie NodeIdentifier bestimmt werden können). |
Der Funktionsbaustein UA_Connect erfordert die folgenden Informationen, um eine Verbindung zu einem lokalen oder remote OPC UA Server herstellen zu können:
- Server URL
- Session Connect Information
Die Server URL besteht grundsätzlich aus einem Präfix, einem Hostnamen und einem Port. Das Präfix beschreibt das OPC‑UA‑Transportprotokoll, das für die Verbindung verwendet werden sollte, z. B. “opc.tcp://” für eine binäre TCP-Verbindung (Standard). Der Hostname bzw. IP-Adressenteil beschreibt die Adressinformationen des OPC‑UA‑Zielservers, z. B. „192.168.1.1“ oder „CX-12345“. Die Portnummer ist der Zielport des OPC UA Servers, z. B. „4840“. Insgesamt kann die Server URL dann wie folgt aussehen: opc.tcp://CX-12345:4840.
Deklaration:
(* Declarations for UA_Connect *)
fbUA_Connect : UA_Connect;
SessionConnectInfo : ST_UASessionConnectInfo;
nConnectionHdl : DWORD;
(* Declarations for UA_Disconnect *)
fbUA_Disconnect : UA_Disconnect;
(* Declarations for state machine and output handling *)
iState : INT;
bDone : BOOL;
bBusy : BOOL;
bError : BOOL;
nErrorID : DWORD;
Implementierung:
CASE iState OF
0:
bError := FALSE;
nErrorID := 0;
SessionConnectInfo.tConnectTimeout := T#1M;
SessionConnectInfo.tSessionTimeout := T#1M;
SessionConnectInfo.sApplicationName := "";
SessionConnectInfo.sApplicationUri := "";
SessionConnectInfo.eSecurityMode := eUASecurityMsgMode_None;
SessionConnectInfo.eSecurityPolicyUri := eUASecurityPolicy_None;
SessionConnectInfo.eTransportProfileUri := eUATransportProfileUri_UATcp;
stNodeAddInfo.nIndexRangeCount := nIndexRangeCount;
stNodeAddInfo.stIndexRange := stIndexRange;
iState := iState + 1;
1:
fbUA_Connect(
Execute := TRUE,
ServerURL := "opc.tcp://192.168.1.1:4840",
SessionConnectInfo := SessionConnectInfo,
Timeout := T#5S,
ConnectionHdl => nConnectionHdl);
IF NOT fbUA_Connect.Busy THEN
fbUA_Connect(Execute := FALSE);
IF NOT fbUA_Connect.Error THEN
iState := iState + 1;
ELSE
bError := TRUE;
nErrorID := fbUA_Connect.ErrorID;
nConnectionHdl := 0;
iState := 0;
END_IF
END_IF
2:
fbUA_Disconnect(
Execute := TRUE,
ConnectionHdl := nConnectionHdl);
IF NOT fbUA_Disconnect.Busy THEN
fbUA_Disconnect(Execute := FALSE);
IF NOT fbUA_Disconnect.Error THEN
iState := 0;
ELSE
bError := TRUE;
nErrorID := fbUA_Disconnect.ErrorID;
iState := 0;
nConnectionHdl := 0;
END_IF
END_IF
END_CASE
Lesen von Variablen
Im nachfolgenden Abschnitt wird beschrieben, wie Sie die Funktionsbausteine TcX_PLCopen_OpcUa verwenden, um einen OPC‑UA‑Knoten von einem lokalen oder remote OPC UA Server auszulesen. Die folgenden Funktionsbausteine sind erforderlich, um eine Verbindung zu einem OPC UA Server herzustellen, UA-Knoten auszulesen und später die Sitzung zu unterbrechen: UA_Connect, UA_GetNamespaceIndex, UA_NodeGetHandle, UA_Read, UA_NodeReleaseHandle, UA_Disconnect.
Der schematische Arbeitsablauf jedes TwinCAT OPC UA Clients kann in drei verschiedene Phasen kategorisiert werden: Preparation, Work und Cleanup.
Der in diesem Abschnitt beschriebene Verwendungsfall kann wie folgt visualisiert werden:

- Der Funktionsbaustein UA_Connect erfordert die folgenden Informationen, um eine Verbindung zu einem lokalen oder entfernten OPC UA Server herstellen zu können (siehe auch Wie eine Verbindung hergestellt wird):
- Server URL
- Session Connect Information
- Der Funktionsbaustein UA_GetNamespaceIndex erfordert einen Connection Handle (von UA_Connect) und einen NamespaceURI zur Auflösung in einen NamespaceIndex, der später von UA_NodeGetHandle verwendet wird, um einen Knotenhandle zu erfassen (siehe auch Wie Kommunikationsparameter zu bestimmen sind).
- Der Funktionsbaustein UA_NodeGetHandle erfordert einen Connection Handle (von UA_Connect) und die NodeID (von ST_UANodeID), um einen Knotenhandle zu erfassen (siehe auch Wie Kommunikationsparameter zu bestimmen sind).
- Der Funktionsbaustein UA_Read erfordert einen Connection Handle (von UA_Connect), einen Knotenhandle (von UA_NodeGetHandle) und einen Zeiger zur Zielvariablen (wo der ausgelesene Wert gespeichert werden sollte). Stellen Sie dabei sicher, dass die Zielvariable den korrekten Datentyp aufweist (siehe auch Wie Kommunikationsparameter zu bestimmen sind).
- Der Funktionsbaustein UA_NodeReleaseHandle erfordert einen Connection Handle (von UA_Connect) und einen Knotenhandle (von UA_NodeGetHandle).
Deklaration:
(* Declarations for UA_GetNamespaceIndex *)
fbUA_GetNamespaceIndex : UA_GetNamespaceIndex;
nNamespaceIndex : UINT;
(* Declarations for UA_NodeGetHandle *)
fbUA_NodeGetHandle : UA_NodeGetHandle;
NodeID : ST_UANodeID;
nNodeHdl : DWORD;
(* Declarations for UA_Read *)
fbUA_Read : UA_Read;
stIndexRange : ARRAY [1..nMaxIndexRange] OF ST_UAIndexRange;
nIndexRangeCount : UINT;
stNodeAddInfo : ST_UANodeAdditionalInfo;
sNodeIdentifier : STRING(MAX_STRING_LENGTH) := 'MAIN.nCounter';
nReadData : INT;
cbDataRead : UDINT;
(* Declarations for UA_NodeReleaseHandle *)
fbUA_NodeReleaseHandle : UA_NodeReleaseHandle;
Implementierung:
CASE iState OF
0:
[...]
2: (* GetNS Index *)
fbUA_GetNamespaceIndex(
Execute := TRUE,
ConnectionHdl := nConnectionHdl,
NamespaceUri := sNamespaceUri,
NamespaceIndex => nNamespaceIndex
);
IF NOT fbUA_GetNamespaceIndex.Busy THEN
fbUA_GetNamespaceIndex(Execute := FALSE);
IF NOT fbUA_GetNamespaceIndex.Error THEN
iState := iState + 1;
ELSE
bError := TRUE;
nErrorID := fbUA_GetNamespaceIndex.ErrorID;
iState := 6;
END_IF
END_IF
3: (* UA_NodeGetHandle *)
NodeID.eIdentifierType := eUAIdentifierType_String;
NodeID.nNamespaceIndex := nNamespaceIndex;
NodeID.sIdentifier := sNodeIdentifier;
fbUA_NodeGetHandle(
Execute := TRUE,
ConnectionHdl := nConnectionHdl,
NodeID := NodeID,
NodeHdl => nNodeHdl);
IF NOT fbUA_NodeGetHandle.Busy THEN
fbUA_NodeGetHandle(Execute := FALSE);
IF NOT fbUA_NodeGetHandle.Error THEN
iState := iState + 1;
ELSE
bError := TRUE;
nErrorID := fbUA_NodeGetHandle.ErrorID;
iState := 6;
END_IF
END_IF
4: (* UA_Read *)
fbUA_Read(
Execute := TRUE,
ConnectionHdl := nConnectionHdl,
NodeHdl := nNodeHdl,
cbData := SIZEOF(nReadData),
stNodeAddInfo := stNodeAddInfo,
pVariable := ADR(nReadData));
IF NOT fbUA_Read.Busy THEN
fbUA_Read( Execute := FALSE, cbData_R => cbDataRead);
IF NOT fbUA_Read.Error THEN
iState := iState + 1;
ELSE
bError := TRUE;
nErrorID := fbUA_Read.ErrorID;
iState := 6;
END_IF
END_IF
5: (* Release Node Handle *)
fbUA_NodeReleaseHandle(
Execute := TRUE,
ConnectionHdl := nConnectionHdl,
NodeHdl := nNodeHdl);
IF NOT fbUA_NodeReleaseHandle.Busy THEN
fbUA_NodeReleaseHandle(Execute := FALSE);
IF NOT fbUA_NodeReleaseHandle.Error THEN
iState := iState + 1;
ELSE
bError := TRUE;
nErrorID := fbUA_NodeReleaseHandle.ErrorID;
iState := 6;
END_IF
END_IF
6:
[...]
END_CASE
Schreiben von Variablen
Im nachfolgenden Abschnitt wird beschrieben, wie Sie die Funktionsbausteine TcX_PLCopen_OpcUa verwenden, um Werte in einem OPC‑UA‑Knoten von einem lokalen oder remote OPC UA Server zu schreiben. Die folgenden Funktionsbausteine sind erforderlich, um eine Verbindung zu einem OPC UA Server herzustellen, UA-Knoten zu schreiben und später die Sitzung zu unterbrechen: UA_Connect, UA_GetNamespaceIndex, UA_NodeGetHandle, UA_Write, UA_NodeReleaseHandle, UA_Disconnect.
Der schematische Arbeitsablauf jedes TwinCAT OPC UA Clients kann in drei verschiedene Phasen kategorisiert werden: Preparation, Work und Cleanup.
Der in diesem Abschnitt beschriebene Verwendungsfall kann wie folgt visualisiert werden:

- Der Funktionsbaustein UA_Connect erfordert die folgenden Informationen, um eine Verbindung zu einem lokalen oder remote OPC UA Server herstellen zu können (siehe auch Wie eine Verbindung hergestellt wird):
- Server URL
- Session Connect Information
- Der Funktionsbaustein UA_GetNamespaceIndex erfordert einen Connection Handle (von UA_Connect) und einen NamespaceURI zur Auflösung in einen NamespaceIndex, der später von UA_NodeGetHandle verwendet wird, um einen Knotenhandle zu erfassen (siehe auch Wie Kommunikationsparameter zu bestimmen sind).
- Der Funktionsbaustein UA_NodeGetHandle erfordert einen Connection Handle (von UA_Connect) und die NodeID (von ST_UANodeID), um einen Knotenhandle zu erfassen (siehe auch Wie Kommunikationsparameter zu bestimmen sind).
- Der Funktionsbaustein UA_Write erfordert einen Connection Handle (von UA_Connect), einen Knotenhandle (von UA_NodeGetHandle) und einen Zeiger zu einer Variablen, die den Wert enthält, der geschrieben werden soll. Stellen Sie dabei sicher, dass die Zielvariable den korrekten Datentyp aufweist (siehe auch Wie Kommunikationsparameter zu bestimmen sind).
- Der Funktionsbaustein UA_NodeReleaseHandle erfordert einen Connection Handle (von UA_Connect) und einen Knotenhandle (von UA_NodeGetHandle).
Deklaration:
(* Declarations for UA_GetNamespaceIndex *)
fbUA_GetNamespaceIndex : UA_GetNamespaceIndex;
nNamespaceIndex : UINT;
(* Declarations for UA_NodeGetHandle *)
fbUA_NodeGetHandle : UA_NodeGetHandle;
NodeID : ST_UANodeID;
nNodeHdl : DWORD;
(* Declarations for UA_Write *)
fbUA_Write : UA_Write;
stIndexRange : ARRAY [1..nMaxIndexRange] OF ST_UAIndexRange;
nIndexRangeCount : UINT;
stNodeAddInfo : ST_UANodeAdditionalInfo;
sNodeIdentifier: STRING(MAX_STRING_LENGTH) := 'MAIN.nNumber';
nWriteData: INT := 42;
(* Declarations for UA_NodeReleaseHandle *)
fbUA_NodeReleaseHandle : UA_NodeReleaseHandle;
Implementierung:
CASE iState OF
0:
[...]
2: (* GetNS Index *)
fbUA_GetNamespaceIndex(
Execute := TRUE,
ConnectionHdl := nConnectionHdl,
NamespaceUri := sNamespaceUri,
NamespaceIndex => nNamespaceIndex
);
IF NOT fbUA_GetNamespaceIndex.Busy THEN
fbUA_GetNamespaceIndex(Execute := FALSE);
IF NOT fbUA_GetNamespaceIndex.Error THEN
iState := iState + 1;
ELSE
bError := TRUE;
nErrorID := fbUA_GetNamespaceIndex.ErrorID;
iState := 6;
END_IF
END_IF
3: (* UA_NodeGetHandle *)
NodeID.eIdentifierType := eUAIdentifierType_String;
NodeID.nNamespaceIndex := nNamespaceIndex;
NodeID.sIdentifier := sNodeIdentifier;
fbUA_NodeGetHandle(
Execute := TRUE,
ConnectionHdl := nConnectionHdl,
NodeID := NodeID,
NodeHdl => nNodeHdl);
IF NOT fbUA_NodeGetHandle.Busy THEN
fbUA_NodeGetHandle(Execute := FALSE);
IF NOT fbUA_NodeGetHandle.Error THEN
iState := iState + 1;
ELSE
bError := TRUE;
nErrorID := fbUA_NodeGetHandle.ErrorID;
iState := 6;
END_IF
END_IF
4: (* UA_Write *)
fbUA_Write(
Execute := TRUE,
ConnectionHdl := nConnectionHdl,
NodeHdl := nNodeHdl,
stNodeAddInfo := stNodeAddInfo,
cbData := SIZEOF(nWriteData),
pVariable := ADR(nWriteData));
IF NOT fbUA_Write.Busy THEN
fbUA_Write(
Execute := FALSE,
pVariable := ADR(nWriteData));
IF NOT fbUA_Write.Error THEN
iState := iState + 1;
ELSE
bError := TRUE;
nErrorID := fbUA_Write.ErrorID;
iState := 6;
END_IF
END_IF
5: (* Release Node Handle *)
fbUA_NodeReleaseHandle(
Execute := TRUE,
ConnectionHdl := nConnectionHdl,
NodeHdl := nNodeHdl);
IF NOT fbUA_NodeReleaseHandle.Busy THEN
fbUA_NodeReleaseHandle(Execute := FALSE);
IF NOT fbUA_NodeReleaseHandle.Error THEN
iState := iState + 1;
ELSE
bError := TRUE;
nErrorID := fbUA_NodeReleaseHandle.ErrorID;
iState := 6;
END_IF
END_IF
6:
[...]
END_CASE
Aufrufen von Methoden
Im nachfolgenden Abschnitt wird beschrieben, wie Sie die Funktionsbausteine TcX_PLCopen_OpcUa verwenden, um Methoden auf einem lokalen oder remote OPC UA Server aufzurufen. Die folgenden Funktionsbausteine sind erforderlich, um eine Verbindung zu einem OPC UA Server herzustellen, UA-Methoden aufzurufen und später die Sitzung zu unterbrechen: UA_Connect, UA_GetNamespaceIndex, UA_MethodGetHandle, UA_MethodCall, UA_MethodReleaseHandle, UA_Disconnect.
Der schematische Arbeitsablauf jedes TwinCAT OPC UA Clients kann in drei verschiedene Phasen kategorisiert werden: Preparation, Work und Cleanup.
Der in diesem Abschnitt beschriebene Verwendungsfall kann wie folgt visualisiert werden:

- Der Funktionsbaustein UA_Connect erfordert die folgenden Informationen, um eine Verbindung zu einem lokalen oder remote OPC UA Server herstellen zu können (siehe auch Wie eine Verbindung hergestellt wird):
- Server URL
- Session Connect Information
- Der Funktionsbaustein UA_GetNamespaceIndex erfordert einen Connection Handle (von UA_Connect) und einen NamespaceURI zur Auflösung in einen NamespaceIndex, der später von UA_NodeGetHandle verwendet wird, um einen Knotenhandle zu erfassen (siehe auch Wie Kommunikationsparameter zu bestimmen sind).
- Der Funktionsbaustein UA_MethodGetHandle erfordert einen Connection Handle (von UA_Connect), eine ObjectNodeID und eine MethodNodeID, um einen Methodenhandle zu erfassen (siehe auch Wie Kommunikationsparameter zu bestimmen sind).
- Der Funktionsbaustein UA_MethodCall erfordert einen Connection Handle (von UA_Connect), einen Methodenhandle (von UA_MethodGetHandle) und Informationen über die Eingangs- und Ausgangsargumente der Methode, die aufgerufen werden soll. Informationen über die Eingangsargumente werden durch die Eingangsparameter pInputArgInfo und pInputArgData von UA_MethodCall repräsentiert. Informationen über die Ausgangsparameter werden durch die pOutputArgInfo und pOutputArgData Eingangsparameter von UA_MethodCall repräsentiert. Der Eingangsparameter pOutputArgInfoAndData stellt dann einen Zeiger zu einer Struktur dar, die die Ergebnisse des Methodenaufrufs enthält, einschließlich aller Ausgangsparameter. In dem nachfolgenden Code-Ausschnitt werden die pInputArgInfo und pInputArgData Parameter in der M_Init-Methode berechnet und erstellt.
- Der Funktionsbaustein UA_NodeReleaseHandle erfordert einen Connection Handle (von UA_Connect) und einen Methodenhandle (von UA_MethodGetHandle).
Initialisierungsmethode M_Init des Funktionsbausteins, der den UA-Methodenaufruf enthält:
MEMSET(ADR(nInputData),0,SIZEOF(nInputData));
nArg := 1;
(********** Input parameter 1 **********)
InputArguments[nArg].DataType := eUAType_Int16;
InputArguments[nArg].ValueRank := -1; (* Scalar = -1 or Array *)
InputArguments[nArg].ArrayDimensions[1] := 0; (* Number of Dimension in case its an array *)
InputArguments[nArg].nLenData := SIZEOF(numberIn1); (* Length if its a STRING *)
IF nOffset + SIZEOF(numberIn1) > nInputArgSize THEN
bInputDataError := TRUE;
RETURN;
ELSE
MEMCPY(ADR(nInputData)+nOffset,ADR(numberIn1),SIZEOF(numberIn1)); (* VALUE in BYTES FORM *)
nOffset := nOffset + SIZEOF(numberIn1);
END_IF
nArg := nArg + 1;
(********** Input parameter 2 **********)
InputArguments[nArg].DataType := eUAType_Int16;
InputArguments[nArg].ValueRank := -1; (* Scalar = -1 or Array *)
InputArguments[nArg].ArrayDimensions[1] := 0; (* Number of Dimension in case its an array *)
InputArguments[nArg].nLenData := SIZEOF(numberIn2); (* Length if its a STRING *)
IF nOffset + SIZEOF(numberIn2) > nInputArgSize THEN
bInputDataError := TRUE;
RETURN;
ELSE
MEMCPY(ADR(nInputData)+nOffset,ADR(numberIn2),SIZEOF(numberIn2));(* VALUE in BYTES FORM *)
nOffset := nOffset + SIZEOF(numberIn2);
END_IF
cbWriteData := nOffset;
Deklaration:
(* Declarations for UA_GetNamespaceIndex *)
fbUA_GetNamespaceIndex : UA_GetNamespaceIndex;
nNamespaceIndex : UINT;
(* Declarations for UA_MethodGetHandle *)
fbUA_MethodGetHandle: UA_MethodGetHandle;
ObjectNodeID: ST_UANodeID;
MethodNodeID: ST_UANodeID;
nMethodHdl: DWORD;
(* Declarations for UA_MethodCall *)
fbUA_MethodCall: UA_MethodCall;
sObjectNodeIdIdentifier : STRING(MAX_STRING_LENGTH) := 'MAIN.fbMathematics';
sMethodNodeIdIdentifier : STRING(MAX_STRING_LENGTH) := 'MAIN.fbMathematics#M_Mul';
nAdrWriteData: PVOID;
numberIn1: INT := 42; // change according to input value and data type
numberIn2: INT := 42; // change according to input value and data type
numberOutPro: DINT; // result (output parameter of M_Mul())
cbWriteData: UDINT; // calculated automatically by M_Init()
InputArguments: ARRAY[1..2] OF ST_UAMethodArgInfo; // change according to input parameters
stOutputArgInfo: ARRAY[1..1] OF ST_UAMethodArgInfo; // change according to output parameters
stOutputArgInfoAndData: ST_OutputArgInfoAndData;
nInputData: ARRAY[1..4] OF BYTE; // numberIn1(INT16)(2) + numberIn2(INT16)(2)
nOffset: UDINT; // calculated by M_Init()
nArg: INT; // used by M_Init()
(* Declarations for UA_MethodReleaseHandle *)
fbUA_MethodReleaseHandle: UA_MethodReleaseHandle;
Implementierung:
CASE iState OF
0:
[...]
2: (* GetNS Index *)
fbUA_GetNamespaceIndex(
Execute := TRUE,
ConnectionHdl := nConnectionHdl,
NamespaceUri := sNamespaceUri,
NamespaceIndex => nNamespaceIndex);
IF NOT fbUA_GetNamespaceIndex.Busy THEN
fbUA_GetNamespaceIndex(Execute := FALSE);
IF NOT fbUA_GetNamespaceIndex.Error THEN
iState := iState + 1;
ELSE
bError := TRUE;
nErrorID := fbUA_GetNamespaceIndex.ErrorID;
iState := 7;
END_IF
END_IF
3: (* Get Method Handle *)
ObjectNodeID.eIdentifierType := eUAIdentifierType_String;
ObjectNodeID.nNamespaceIndex := nNamespaceIndex;
ObjectNodeID.sIdentifier := sObjectNodeIdIdentifier;
MethodNodeID.eIdentifierType := eUAIdentifierType_String;
MethodNodeID.nNamespaceIndex := nNamespaceIndex;
MethodNodeID.sIdentifier := sMethodNodeIdIdentifier;
M_Init();
IF bInputDataError = FALSE THEN
iState := iState + 1;
ELSE
bBusy := FALSE;
bError := TRUE;
nErrorID := 16#70A; //out of memory
END_IF
4: (* Method Get Handle *)
fbUA_MethodGetHandle(
Execute := TRUE,
ConnectionHdl := nConnectionHdl,
ObjectNodeID := ObjectNodeID,
MethodNodeID := MethodNodeID,
MethodHdl => nMethodHdl);
IF NOT fbUA_MethodGetHandle.Busy THEN
fbUA_MethodGetHandle(Execute := FALSE);
IF NOT fbUA_MethodGetHandle.Error THEN
iState := iState + 1;
ELSE
bError := TRUE;
nErrorID := fbUA_MethodGetHandle.ErrorID;
iState := 6;
END_IF
END_IF
5: (* Method Call *)
stOutputArgInfo[1].nLenData := SIZEOF(stOutputArgInfoAndData.pro);
fbUA_MethodCall(
Execute := TRUE,
ConnectionHdl := nConnectionHdl,
MethodHdl := nMethodHdl,
nNumberOfInputArguments := nNumberOfInputArguments,
pInputArgInfo := ADR(InputArguments),
cbInputArgInfo := SIZEOF(InputArguments),
pInputArgData := ADR(nInputData),
cbInputArgData := cbWriteData,
pInputWriteData := 0,
cbInputWriteData := 0,
nNumberOfOutputArguments := nNumberOfOutputArguments,
pOutputArgInfo := ADR(stOutputArgInfo),
cbOutputArgInfo := SIZEOF(stOutputArgInfo),
pOutputArgInfoAndData := ADR(stOutputArgInfoAndData),
cbOutputArgInfoAndData := SIZEOF(stOutputArgInfoAndData));
IF NOT fbUA_MethodCall.Busy THEN
fbUA_MethodCall(Execute := FALSE);
IF NOT fbUA_MethodCall.Error THEN
iState := iState + 1;
numberOutPro := stOutputArgInfoAndData.pro;
ELSE
bError := TRUE;
nErrorID := fbUA_MethodCall.ErrorID;
iState := 6;
END_IF
END_IF
6: (* Release Method Handle *)
fbUA_MethodReleaseHandle(
Execute := TRUE,
ConnectionHdl := nConnectionHdl,
MethodHdl := nMethodHdl);
IF NOT fbUA_MethodReleaseHandle.Busy THEN
fbUA_MethodReleaseHandle(Execute := FALSE);
bBusy := FALSE;
IF NOT fbUA_MethodReleaseHandle.Error THEN
iState := 7;
ELSE
bError := TRUE;
nErrorID := fbUA_MethodReleaseHandle.ErrorID;
iState := 7;
END_IF
END_IF
7:
[...]
END_CASE