Beckhoff#TwinCAT3 TF6100 OPCUA _Part4_PLCOPEN Library_MethodCall

みんなさん最近元気ですか?もうBeckhoff TF6100 第5話になりましたね。前回はPLC OPENライブラリ経由でOPC ServerのNodeを読み書きしました。次はMethod Callになりますね。ちなみにこちらは第4話です。

Beckhoff#TwinCAT3 TF6100 OPCUA _Part3_PLCOPEN Library

Method Serviceとは?

OPC UAのMethod Callに関してはこのLINKして詳しい説明があります。

https://reference.opcfoundation.org/v104/Core/docs/Part4/5.11.2/

まず簡単に言いますと、

This Service is used to call (invoke) a list of Methods.

This Service provides for passing input and output arguments to/from a Method. These arguments are defined by Properties of the Method.

つまるこのServiceはMethodを呼び出し、InputとOutput Argumentsが提供されます。それらのパラメタはMethodのプロパティ(Properties)から決めます。

そしてMethodは関数だと思ってください。

パラメタ

Methodを呼び出すためにはいくつのパラメタが必要となり、Methodの戻り値にもいくつがあります。

https://reference.opcfoundation.org/v104/Core/docs/Part4/5.11.2/#Table65

Request

こちらはRequestつまりInputです。右側にオレンジについてるパラメタではBeckhoffのFunction Blockで自分で構成する必要があります。(変数名は100%一致しないですが)

requestHeaderRequestHeader
methodsToCall []CallMethodRequestO
objectIdNodeIdO
methodIdNodeIdO
inputArguments []BaseDataTypeO

Response

こちらはResponseつまりOutputになります。右側にオレンジについてるパラメタではBeckhoffのFunction BlockのOutputになります。(変数名は100%一致しないですが)

responseHeaderResponseHeader
results []CallMethodResultO
statusCodeStatusCodeO
inputArgumentResults []StatusCodeO
inputArgumentDiagnosticInfos []DiagnosticInfoO
outputArguments []BaseDataTypeO
diagnosticInfos []DiagnosticInfoO


Support DataType

Method Call

Beckhoff TF6100でMethod Callを実装するにはもちろんOPCUA Serverと接続のUA_Connect・UA_Disconnectにも必要ですが、ほかにもUA_MethodGetHandle・UA_MethodReleaseHandle・UA_MethodCallにも使われています。

ですが、前のPOST見た方にはNodeの読み書きの流れとはあまり変わりません。

流れ

OPCUA Serverと接続するまでいつも通りです。そしてMethod CallするにはUA_MethodGetHandleからMethodの情報・Objectの情報などを構成します。

UA_MethodCallをInput Arguments(pInputArgInfo,pInputArgData),Output Arguments(pOutputArgInfo,pOutputArgData)を構成しMethodを呼び出します。

Methodからの戻り値がpOutputArgInfoAndDataに入ります。pOutputArgInfoAndData

はPointerでMethodの戻り値の構造体が入っています。

最後にUA_MethodReleaseHandleを呼び出し、MethodのConnectionを解放します。

Method Call テスト

UaExpertからCallしたいMethodを右クリックし、Call..をクリックします。

値など入れCallボタン押せばOKです。


UA_MethodGetHandle

このFunctionBlockはUA_MethodCallを使用するためのMethodHdIを作成します。

VAR_INPUT
ExecuteBOOL立ち上げてコマンド実行
ConnectionHdlDWORDOPCUA Method  Handle
ObjectIDST_UANodeIDObject Node ID
MethodNodeIDST_UANodeIDMethod NodeID
TimeoutTIMETimeout設定
VAR_OUTPUT
MethodHdlDWORDOPC UA Method Handle、Method Callなどで使う変数です。
DoneBOOLエラーなしで実行OK
BusyBOOL実行中
ErrorBOOLエラーあり
ErrorIDDWORDエラーID


ST_UANodeID

nNamespaceIndexUINTUA_GetNamespaceIndexからのNamespaceIndex
nReservedARRAY [1..2] OF BYTE
sIdentifierSTRING(MAX_STRING_LENGTH)NodeIDのIdentifierです。今回はDemo.Methodになります。
eIdentifierTypeE_UAIdentifierTypeNodeIdのIdentifier種類です。文字列か、数字か。今回は文字列です。

UA_MethodCall

かなり長いFunction Blockですが、これはUA_MethodCallになります。このFunction Blockを使ってMethodを呼び出します。

VAR_INPUT
ExecuteBOOL立ち上げてコマンド実行
ConnectionHdlDWORDOPCUA Connection Handle
MethodHdlDWORDUA_MethodGetHandleから
nNumberOfInputArgumentsUDINTInputArgumentsの数
pInputArgInfoPOINTER TO ST_UAMethodArgInfoInputArgumentsの情報を保存する変数のMemoryアドレス
cbInputArgInfoUDINTInputArgumentsの情報を保存する変数のMemory Size
pInputArgDataPVOIDInputArgumentsの値を保存する変数のMemoryアドレス(固定長さ)
cbInputArgDataUDINTInputArgumentsの値を保存する変数のMemory Size(固定長さ)
pInputWriteDataPVOIDInputArgumentsの値を保存する変数のMemoryアドレス(可変長さ)
cbInputWriteDataUDINTInputArgumentsの値を保存する変数のMemory Size(可変長さ)
nNumberOfOutputArgumentsUDINTOutputArgumentsの数
pOutputArgInfoPOINTER TO ST_UAMethodArgInfoOutputArgumentsの情報を保存する変数のMemoryアドレス
cbOutputArgInfoUDINTOutputArgumentsの値を保存する変数のMemory Size
pOutputArgInfoAndDataPVOIDOutputArgumentsの値(バイド配列)のMemoryアドレス
cbOutputArgInfoAndDataUDINTOutputArgumentsの値(バイド配列)のMemory Size
TimeoutTIME
VAR_OUTPUT
cbRead_RUDINTTotal SeverからのデータByte数
DoneBOOLエラーなしで実行OK
BusyBOOL実行中
ErrorBOOLエラーあり
ErrorIDDWORDエラーID


ST_UAMethodArgInfo

DataTypeE_UADataTypeMethodのUA Type
ValueRankDINTパラメタががScalarか配列か
ArrayDimensionsARRAY[1..3] OF UDINT配列ならその構造
nLenDataDINTArguments変数の長さ(構造体だけは必要になります。)

UA_MethodReleaseHandle

VAR_INPUT
ExecuteBOOL立ち上げてコマンド実行
ConnectionHdlDWORDOPCUA Connection Handle
MethodHdlDWORDMethod Connection Handle
TimeoutTIMEDisconnect Timeout設定
VAR_OUTPUT
DoneBOOLエラーなしで実行OK
BusyBOOL実行中
ErrorBOOLエラーあり
ErrorIDDWORDエラーID

ST_OutputArgInfoAndData

このDUTは自分で定義する必要があります。実際のMethodに合わせてください。

nNumberOfOutputArgumentsUDINTOutputパラメタの数
nReservedARRAY[1..4] OF BYTE
stOutputArgInfoARRAY[1..1] OF ST_UAMethodArgInfoOutputパラメタの数分のST_UAMethodArgInfoが定義します。もしOutputパラメタ3つあれば配列の構造は[1..4]になります。
proLREAL出力データ。実際Methodに合わせてください。

MEMSET

この関数を使ってPLC変数をメモリエリア特定し指定な値を一括書き込みます。

VAR_INPUT
destAddrDWORD書き込みしたい変数のStart Memoryアドレス
fillByteUSINT書き込みしたいの値
nUINT何Byte書き込みたいのか
Return
UINT0:パラメタエラー、destAddrかnが0になっています。>0:SetされたBytes数が戻り値になります。

MEMCPY

この関数を使ってPLC変数をあるエリアから別のMemoryエリアにCopyできます。

VAR_INPUT
destAddrDWORD転送先のStart Memoryアドレス
srcAddrUSINT転送元のStart Memoryアドレス
nUINT何Byte転送したいのか
Return
UINT0:パラメタエラー、destAddr、srcAddrかnが0になっています。>0:SetされたBytes数が戻り値になります。

Code

VAR

最初には変数の定義ですね。UA_Connect、UA_GetNamespaceIndex、UA_Disconnectは前と変わっていません。多くなったのはUA_MethodGetHandle、UA_MethodCall、UA_MethodreleaseHandleの部分で、それならの変数をどう使うかプログラムを説明するときに話しします。

VAR

//Connect/DisConnect Functionality
UA_CONNENT :UA_CONNECT;
UASessionInfo :ST_UASessionConnectInfo;
UA_Disconnect :UA_Disconnect;

//NameIndex
UA_GetNamespaceIndex : UA_GetNamespaceIndex;
nNamespaceIndex : UINT;

//UA_MethodGetHandle
UA_MethodGetHandle : UA_MethodGetHandle;
ObjectNodeID : ST_UANodeID;
MethodNodeID : ST_UANodeID;
nMethodHdl : DWORD;

//UA_MethodCall
UA_MethodCall : UA_MethodCall;
sObjectNodeIdIdentifier : STRING(MAX_STRING_LENGTH) := ‘Demo.Method’;
sMethodNodeIdIdentifier : STRING(MAX_STRING_LENGTH) := ‘Demo.Method.Multiply’;
nAdrWriteData : PVOID;

//Method Parameters
numberIn1 : LREAL := 100.0;
numberIn2 : LREAL := 31.2;
numberOutPro: LREAL;

//UA_MethoCall Configs
cbWriteData : UDINT;
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..16] OF BYTE; // numberDouble(8) + numberIn2(Double)(8)
nOffset : UDINT;

//UA_MethodreleaseHandle
UA_MethodReleaseHandle: UA_MethodReleaseHandle;

//Operating Variables
iState :INT;
iErroStep :INT;
bError :BOOL;
bDone :BOOL;
bBusy :BOOL;
bReset :BOOL;
bInit :BOOL;
nErrorID :DWORD;
nConnectionHdi :DWORD;
bInputDataError :BOOL;

arrTON :ARRAY[0..3]OF TON;
END_VAR

VAR CONSTANT

こちらは定数の定義になります。前と比べると増えたのはMethod CallのStep定義、Method CallのInputArguments数(2個)とOutputArguments数(1個)、あとInputArguments数のMemory Size(Byteで計算、Double2つなので、8×2=16になります。)。

VAR CONSTANT


ciStep_Parameters_Initlize :INT :=0;
ciStep_UAConnect :INT :=10;
ciStep_UAGetNamespaceIndex :INT :=20;
ciStep_UAGetMethodHandleInit :INT :=70;
ciStep_UAGetMethodHandle :INT :=75;
ciStep_UAMethodCall :INT :=80;
ciStep_UAMethodRelease :INT :=90;
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′;
ciScalar INT :=-1;
ciNumberOfInputArguments : UDINT := 2; // change to number of input parameters
ciNumberOfOutputArguments : UDINT := 1; // change to number of output parameters
ciInputArgSize : INT := 16;

ciUAMethodHandleInitMemoryError :DWORD:=16#70AA;

END_VAR

PROGRAM

Step間の制御はCASE文を使っています。

  • Flag,Function初期化
  • Serverと接続
  • NameSpaceIndexHandle取る
  • Method パラメタ初期化
  • MethodHandle取る
  • MethodHandleリリース
  • ServerとDisconnect

の流れです。

Blockに実行エラーがあると、エラーCodeを記録しErrorStepに飛び、ResetがされたたらまたFlag,Function初期化から始まります。多分も絵描かなくてもわかると思います。

Method Example

今回は前と同じくOPC Demo Serverを使用します。UAExpertから接続しNodeをみますとDemo>003_Method>MultiplyというMethodがあります。プログラムの例ではこのMethodを呼び出します。


ciStep_Parameters_Initlize

Flag,Function初期化です。

ciStep_Parameters_Initlize:

IF bReset THEN
bReset:=FALSE;
END_IF;

bError :=FALSE;
bDone :=FALSE;
bBusy :=FALSE;
bInputDataError:=False;
nErrorID:=0;
iErroStep:=0;

UASessionInfo.tConnectTimeout:=ctUASession_Connect_Timeout;
UASessionInfo.tSessionTimeout:=ctUASession_Session_Timeout;
UASessionInfo.eSecurityMode:=eUASecurityMsgMode_None;
UASessionInfo.eSecurityPolicyUri:=eUASecurityPolicy_None;
UASessionInfo.eTransportProfileUri:=eUATransportProfileUri_UATcp;


UA_CONNENT(
Execute:=FALSE
);
UA_Disconnect(
Execute:=FALSE
);
UA_GetNamespaceIndex(
Execute:=FALSE
);
UA_MethodGetHandle(
Execute:=FALSE
);
UA_MethodCall(
Execute:=FALSE
);
UA_MethodReleaseHandle(
Execute:=FALSE
);
IF NOT UA_CONNENT.Busy
AND NOT UA_Disconnect.Busy
AND NOT UA_GetNamespaceIndex.Busy
AND NOT UA_MethodGetHandle.Busy
AND NOT UA_MethodCall.Busy
AND NOT UA_MethodReleaseHandle.Busy
THEN
iState:=ciStep_UAConnect;
END_IF

ciStep_UAConnect

Serverと接続します。

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
UA_CONNENT(
Execute:=FALSE
);
iState:=ciStep_UAGetNamespaceIndex;
ELSIF UA_CONNENT.Error THEN
iState:=ciStep_UAError;
nErrorID:=UA_CONNENT.ErrorID;
iErroStep:=ciStep_UAConnect;
END_IF

ciStep_UAGetNamespaceIndex

NameSpaceIndexHandle取ります。

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_UAGetMethodHandleInit;
ELSIF UA_GetNamespaceIndex.Error THEN
iState:=ciStep_UAError;
nErrorID:=UA_GetNamespaceIndex.ErrorID;
iErroStep:=ciStep_UAGetNamespaceIndex;
END_IF

ciStep_UAGetMethodHandleInit

sObjectNodeIdIdentifier

Methodを呼び出すにはObjectIDが必要になります。まず003_MethodのFolderをクリックします。Methodを呼び出すにはObjectIDが必要になります。まず003_MethodのFolderをクリックします。

右にあるNodeIdはns=2;s=Demo.Methodの値がありますね。

つまり、Namespace=2,sはStringで定義、その値はDemo.Methodになりますね。

なので、ObjectIDはDemo.Methodです。

もちろん、下にIdentifierの項目から直接Copyしてもいいです。

sMethodNodeIdIdentifier

次はNodeIdですね。003_Methodの下にあるのMultiplyをクリックします。

右にあるNodeIdはns=2;s=Demo.Method.Multiplyの値がありますね。

つまり、Namespace=2,sはStringで定義、その値はDemo.Method.Multiplyになりますね。

なので、MethodIDはDemo.Method.Multiplyです。

もちろん、下にIdentifierの項目から直接Copyしてもいいです。

Input/Output Arguments

各Methodにも自分のInputArgumentsとOutputArgumentsがあります。例えば、MultiplyのInputArgumentsをクリックします。

中にInputArgumentsが何個あるかわかります。

下図の例だと2つですね。Argument Array[2]なので。

Data Type・ValveRank

InputArgumentsのDataTypeにも重要です。Value>DataType>Identifierには下図の例だと11[Double]が示しています。OPCUA のDoubleだとBeckhoffのLREALに対応します。

一覧表は最初にあるSupport Data Typeに一覧があります。

Methodのコンセプトわかった時点で次は実際Initなどはなにかやるかを絵で説明します。

ciStep_UAGetMethodHandleInit:

ObjectNodeID.eIdentifierType:=eUAIdentifierType_String;
ObjectNodeID.nNamespaceIndex:=nNamespaceIndex;
ObjectNodeID.sIdentifier:=sObjectNodeIdIdentifier;

MethodNodeID.eIdentifierType:=eUAIdentifierType_String;
MethodNodeID.nNamespaceIndex:=nNamespaceIndex;
MethodNodeID.sIdentifier:=sMethodNodeIdIdentifier;

nOffset:=0;

MEMSET(
destAddr:=ADR(nInputData)
,fillByte:=0
,n:=SIZEOF(nInputData)
);

InputArguments[1].DataType:=eUAType_Double;
InputArguments[1].ValueRank:=ciScalar;
InputArguments[1].ArrayDimensions[1]:=0;
InputArguments[1].nLenData:=SIZEOF(numberIn1);

IF nOffset +SIZEOF(numberIn1) > ciInputArgSize THEN
bInputDataError:=TRUE;
nErrorID:=ciUAMethodHandleInitMemoryError;
iState:=ciStep_UAError;
iErroStep:=ciStep_UAGetMethodHandleInit;
ELSE
MEMCPY(
destAddr:=ADR(nInputData)+nOffset
,srcAddr:=ADR(numberIn1)
,n:=SIZEOF(numberIn1)
);
nOffset:=nOffset+SIZEOF(numberIn1);
END_IF

InputArguments[2].DataType:=eUAType_Double;
InputArguments[2].ValueRank:=ciScalar;
InputArguments[2].ArrayDimensions[2]:=0;
InputArguments[2].nLenData:=SIZEOF(numberIn2);

IF nOffset+SIZEOF(numberIn2) > ciInputArgSize THEN
bInputDataError:=TRUE;
nErrorID:=ciUAMethodHandleInitMemoryError;
iState:=ciStep_UAError;
iErroStep:=ciStep_UAGetMethodHandleInit;
ELSE
MEMCPY(
destAddr:=ADR(nInputData)+nOffset
,srcAddr:=ADR(numberIn2)
,n:=SIZEOF(numberIn2)
);
nOffset:=nOffset+SIZEOF(numberIn2);
cbWriteData:=nOffset;
iState:=ciStep_UAGetMethodHandle;
END_IF

ciStep_UAGetMethodHandle

MethodHandleを取ります。

ciStep_UAGetMethodHandle:
UA_MethodGetHandle(
Execute:=TRUE
,ConnectionHdl:=nConnectionHdi
,ObjectNodeID:=ObjectNodeID
,MethodNodeID:=MethodNodeID
,MethodHdl=>nMethodHdl
);
IF UA_MethodGetHandle.Done AND NOT UA_MethodGetHandle.Error THEN
UA_MethodGetHandle(
Execute:=FALSE
);
iState:=ciStep_UAMethodCall;
ELSIF UA_MethodGetHandle.Error THEN
iState:=ciStep_UAError;
nErrorID:=UA_MethodGetHandle.ErrorID;
iErroStep:=ciStep_UAGetMethodHandle;
END_IF

ciStep_UAMethodCall

最初にはMethod出力のMemory Sizeだけ定義しそのあとはUA_MethodCallします。

ciStep_UAMethodCall:
stOutputArgInfo[1].nLenData:=SIZEOF(stOutputArgInfoAndData.pro);


UA_MethodCall(
Execute:=TRUE
,ConnectionHdl:=nConnectionHdi
,MethodHdl:=nMethodHdl
,nNumberOfInputArguments:=ciNumberOfInputArguments
,pInputArginfo:=ADR(InputArguments)
,cbInputArginfo:=SIZEOF(InputArguments)
,pInputArgData:=ADR(nInputData)
,cbInputArgData:=cbWriteData
,pInputWriteData:=0
,cbInputWriteData:=0
,nNumberOfOutputArguments:=ciNumberOfOutputArguments
,pOutputArgInfo:=ADR(stOutputArgInfo)
,cbOutputArgInfo:=SIZEOF(stOutputArgInfo)
,pOutputArgInfoAndData:=ADR(stOutputArgInfoAndData)
,cbOutputArgInfoAndData:=SIZEOF(stOutputArgInfoAndData)
);
IF UA_MethodCall.Done AND NOT UA_MethodCall.Error THEN
UA_MethodCall(
Execute:=FALSE
);
iState:=ciStep_UAMethodCall;
numberOutPro := stOutputArgInfoAndData.pro;
ELSIF UA_MethodCall.Error THEN
iState:=ciStep_UAError;
nErrorID:=UA_MethodCall.ErrorID;
iErroStep:=ciStep_UAMethodCall;
END_IF

ciStep_UAMethodRelease

MethodがCallされたあとリリースします。

ciStep_UAMethodRelease:
UA_MethodReleaseHandle(
Execute:=TRUE
,ConnectionHdl:=nConnectionHdi
,MethodHdl:=nMethodHdl
);
IF UA_MethodReleaseHandle.Done AND NOT UA_MethodReleaseHandle.Error THEN
UA_MethodReleaseHandle(
Execute:=FALSE
);
iState:=ciStep_UAMethodCall;
ELSIF UA_MethodReleaseHandle.Error THEN
iState:=ciStep_UADIsconnect;
nErrorID:=UA_MethodReleaseHandle.ErrorID;
iErroStep:=ciStep_UAMethodRelease;
END_IF

ciStep_UADIsconnect


Connectionを切ります。

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;
iErroStep:=ciStep_UADIsconnect;
END_IF

ciStep_End


2秒のDelayのあと再接続します。

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

エラーが来たらこのStepに飛び、エラー待ちになります。

ciStep_UAError:
bError:=TRUE;
IF bReset THEN
iState:=ciStep_Parameters_Initlize;
END_IF;

結果

Method Callされたら、NumberOutProの結果はnumberIn1かけるnumberIn2になります。

プロジェクトは以下のLinkでダウンロードしてください。

https://github.com/soup01Threes/TwinCAT3/blob/main/TwinCAT%20Project_TF6100_Using_PLCOPEN_LIBRARY_2_CallMethod.tnzip

はーい、お疲れ様です。

もしなにか質問あれば、メール・コメント・Twitterなどでもどうぞ!

Twitterのご相談:@3threes2

メールのご相談:soup01threes*gmail.com (*を@に)

Footer_Basic

Please Support some devices for my blog

Amazon Gift List

Find ME

Twitter:@3threes2
Email:soup01threes*gmail.com (* to @)
YoutubeChannel:https://www.youtube.com/channel/UCQ3CHGAIXZAbeOC_9mjQiWQ

シェアする

  • このエントリーをはてなブックマークに追加

フォローする