今回の記事ではANYの次に、T_ARG Data Typeを紹介します。T_Argは、C#のジェネリックやC++のテンプレートには対応していませんが、IEC 61131-3のジェネリック関数の開発には対応しています。それによって、異なるデータ型やデータ構造に対して同じ関数を使用できるような設計が可能になります。
さ、始めよう。
Reference Link
T_ARG?
T_ArgはTwinCATライブラリTc2_Utilitiesで宣言されており、ANYと違ってTwinCAT 2でも使用できます。 T_Argの構造は、データ型ANYに使用されている構造と同じく、またVAR_IN_OUTの範囲も含め、どの場所でも使用することができます。
TYPE T_Arg : STRUCT eType : E_ArgType := ARGTYPE_UNKNOWN; // Argument data type cbLen : UDINT := 0; // Argument data byte length pData : UDINT := 0; // Pointer to argument data END_STRUCT END_TYPE |
前回のTutorialも紹介しましたが、ANYが使用できる範囲はVAR_INPUTのみです。
E_ArgType
こちらはE_ArgTypeのEnum構造になります。
TYPE E_ArgType : ( ARGTYPE_UNKNOWN := 0, ARGTYPE_BYTE, ARGTYPE_WORD, ARGTYPE_DWORD, ARGTYPE_REAL, ARGTYPE_LREAL, ARGTYPE_SINT, ARGTYPE_INT, ARGTYPE_DINT, ARGTYPE_USINT, ARGTYPE_UINT, ARGTYPE_UDINT, ARGTYPE_STRING, ARGTYPE_BOOL, ARGTYPE_BIGTYPE, ARGTYPE_ULARGE, ARGTYPE_UHUGE, ARGTYPE_LARGE, ARGTYPE_HUGE, ARGTYPE_LWORD ); END_TYPE |
Import Library
LibrarayからTc2_UtilitiesライブラリをImportしてください。
Example1
最初はFunctionを作成し、InputパラメータはT_Argとして宣言し、次は入力パラメータの種類によって文字列を返します。
Program
FC_T_ARG_Test
CASE文を利用し、myArh.eTypeから入力パラメータのデータ・タイプを判断します。
FUNCTION FC_T_ARG_Test : String VAR_INPUT myArg:T_Arg; END_VAR FC_T_ARG_Test:=”; CASE myArg.eType OF E_ArgType.ARGTYPE_BOOL: FC_T_ARG_Test:=’Bool’; E_ArgType.ARGTYPE_BYTE: FC_T_ARG_Test:=’Byte’; E_ArgType.ARGTYPE_DINT: FC_T_ARG_Test:=’DINT’; E_ArgType.ARGTYPE_LREAL: FC_T_ARG_Test:=’LReal’; E_ArgType.ARGTYPE_REAL: FC_T_ARG_Test:=’Real’; E_ArgType.ARGTYPE_INT: FC_T_ARG_Test:=’Int’; E_ArgType.ARGTYPE_DWORD: FC_T_ARG_Test:=’Dword’; END_CASE |
Main
いくつのData Typeの変数を関数に渡してみます。
PROGRAM MAIN VAR wStringVar:STRING; StringVar:WSTRING; BoolVar:BOOL; ByteVar:BYTE; WordVar:WORD; DWordVar:DWORD; LWordVar:LWORD; USIntVar:USINT; IntVar:INT; RealVar:REAL; myReturnStrings:ARRAY[0..99]OF STRING; END_VAR //Example1, return String myReturnStrings[0]:=FC_T_ARG_Test(myArg:=IntVar); myReturnStrings[2]:=FC_T_ARG_Test(myArg:=ByteVar); myReturnStrings[3]:=FC_T_ARG_Test(myArg:=BoolVar); myReturnStrings[4]:=FC_T_ARG_Test(myArg:=RealVar); myReturnStrings[5]:=FC_T_ARG_Test(myArg:=DWordVar); |
Result
でもTwinCATからエラーが出てきました。どうやら関数に渡した変数に問題がありそうです。
Error Listから確認するとCannot convert Type to T_Argのようなエラーメッセージが表示されました。つまりBoolなどの変数を直接にT_ARGに宣言されたパラメータに渡すことができません。
Example2
T_Argとして渡されるパラメータは、最初に初期化する必要があります。ライブラリ Tc2_Utilities には、変数を T_Arg 型の構造体 (F_INT(), F_REAL(), F_DINT(), …) に変換するための関数が含まれていて、一回この関数を使って変数を初期化してください。
Program
Main
F_INT()などの初期化関数から変数を初期化しFC_T_ARG_Testに渡しします。
//Example1, return String myReturnStrings[0]:=FC_T_ARG_Test(myArg:=F_INT(in:=IntVar)); myReturnStrings[2]:=FC_T_ARG_Test(myArg:=F_BYTE(in:=ByteVar)); myReturnStrings[3]:=FC_T_ARG_Test(myArg:=F_BOOL(in:=BoolVar)); myReturnStrings[4]:=FC_T_ARG_Test(myArg:=F_REAL(in:=RealVar)); myReturnStrings[5]:=FC_T_ARG_Test(myArg:=F_DWORD(in:=DWordVar)); |
Result
Done!Data Typeは全部判別できました。
Example3
T_ARGを使用する一番便利な特徴は配列で定義でき、FCやFBにVAR_IN_OUTパラメータとして宣言できます。
Program
Main
下記の例ではT_Argsの配列を定義し、各ElementsをF_XXX関数で初期化し、最後はMEMCPY関数でT_Argsの配列に各Elementsを別の変数に移動します。
PROGRAM VAR arrayArgs :ARRAY[0..99]OF T_Arg; END_VAR IntVar:=1234; ByteVar:=56; BoolVar:=TRUE; RealVar:=3.14; DWordVar:=123412345; arrayArgs[0]:=F_INT(IntVar); arrayArgs[1]:=F_BYTE(byteVar); arrayArgs[2]:=F_BOOL(BoolVar); arrayArgs[3]:=F_REAL(RealVar); arrayArgs[4]:=F_DWORD(DWORDVar); MEMCPY(ADR(myInt),arrayArgs[0].pData,arrayArgs[0].cbLen); MEMCPY(ADR(myBtye),arrayArgs[1].pData,arrayArgs[1].cbLen); MEMCPY(ADR(myBool),arrayArgs[2].pData,arrayArgs[2].cbLen); MEMCPY(ADR(myReal),arrayArgs[3].pData,arrayArgs[3].cbLen); MEMCPY(ADR(myDWORD),arrayArgs[4].pData,arrayArgs[4].cbLen); |
Result
Done!
Example4
今度はFC_T_ARG_Arrayの関数を作成、入力パラメータはT_ARGの配列に宣言します。
次はLOWER_BOUNDとHIGHER_BOUND関数で配列の長さを取得し、FOR LOOPを使って配列の各Elementにアクセス、加算します。
Program
FC_T_ARG_Array
VAR_IN_OUTにはARRAY[*]を可変の配列を宣言します。
FUNCTION FC_T_ARG_Array : DINT VAR_IN_OUT myArray :ARRAY[*]OF T_ARG; END_VAR VAR Buffer:DINT; index:DINT; _varDword:DWORD; _varByte : BYTE; _varusint : USINT; _varuint : UINT; _varint : INT; _vardint : DINT; _varreal : REAL; _varLreal : LREAL; END_VAR Buffer:=0; FOR index :=LOWER_BOUND(myArray,1) TO UPPER_BOUND(myArray,1) DO CASE myArray[index].eType OF E_ArgType.ARGTYPE_BYTE: MEMCPY(ADR(_varByte),myarray[index].pData,myarray[index].cbLen); Buffer:=Buffer+BYTE_TO_DINT(_varByte); E_ArgType.ARGTYPE_SINT: MEMCPY(ADR(_varusint),myarray[index].pData,myarray[index].cbLen); Buffer:=Buffer+USINT_TO_DINT(_varusint); E_ArgType.ARGTYPE_UINT: MEMCPY(ADR(_varuint),myarray[index].pData,myarray[index].cbLen); Buffer:=Buffer+UINT_TO_DINT(_varuint); E_ArgType.ARGTYPE_INT: MEMCPY(ADR(_varint),myarray[index].pData,myarray[index].cbLen); Buffer:=Buffer+UINT_TO_DINT(_varint); E_ArgType.ARGTYPE_REAL: MEMCPY(ADR(_varreal),myarray[index].pData,myarray[index].cbLen); Buffer:=Buffer+REAL_TO_DINT(_varreal); E_ArgType.ARGTYPE_LREAL: MEMCPY(ADR(_varreal),myarray[index].pData,myarray[index].cbLen); Buffer:=Buffer+LREAL_TO_DINT(_varLreal); E_ArgType.ARGTYPE_DWORD: MEMCPY(ADR(_varDword),myarray[index].pData,myarray[index].cbLen); Buffer:=Buffer+DWORD_TO_DINT(_varDword); END_CASE FC_T_ARG_Array:=Buffer; END_FOR |
MAIN
T_ARG配列にある各のIndexのElementにも異なるデータ・タイプの変数を格納します。
PROGRAM VAR TestArray :ARRAY[0..3]OF T_Arg; END_VAR IntVar:=1234; ByteVar:=56; BoolVar:=TRUE; RealVar:=3.14; myDWORD2:=123; TestArray[0]:=F_INT(intvar); TestArray[1]:=F_BYTE(bytevar); TestArray[2]:=F_REAL(realvar); TestArray[3]:=F_DWORD(myDWORD2); myDintResult:=FC_T_ARG_Array(testarray); |
Result
Done!戻り値はTestArray[0]から[3]まで加算されました。
Example5
最後のExampleではT_Arg配列に各Indexにも異なるElementを格納してみます。このコンセントはTF7000 VisionのContainerと似ています。
Program
以下の例ではT_ARGに格納されるElementは汎用 Data typeの変数はもちろん、汎用Data Typeの配列・構造体・構造体の配列も格納することが可能です。F_BIGTYPE関数を使用すれば構造体や連続のByteデータを初期化できます。
MAIN
PROGRAM VAR MyComplexT_ARG_Arrays:ARRAY[0..3]OF T_Arg; r32Array,r32ArrayBuffer:ARRAY [0..5]OF REAL; dutVariable,dutVaraibleBuffer:DUT_MyComplexDUT; dutArray,dutArrayBuffer:ARRAY[0..2]OF DUT_MyComplexDUT; myString,myStringBuffer:STRING; ComapreResult:array[0..9]OF DINT; END_VAR r32Array[0]:=1.0; r32Array[1]:=11.0; r32Array[2]:=21.0; r32Array[3]:=31.0; r32Array[4]:=41.0; r32Array[5]:=51.0; dutVariable.myBool:=TRUE; dutVariable.myByte:=16#FF; dutVariable.myInt:=41; dutVariable.myReal:=5.11; dutArray[0].myBool:=FALSE; dutArray[0].myByte:=51; dutArray[0].myInt:=61; dutArray[0].myReal:=91.2; dutArray[1].myBool:=TRUE; dutArray[1].myByte:=11; dutArray[1].myInt:=611; dutArray[1].myReal:=611.2; dutArray[2].myBool:=FALSE; dutArray[2].myByte:=31; dutArray[2].myInt:=761; dutArray[2].myReal:=791.2; myString:=’Beckhoff1234′; MyComplexT_ARG_Arrays[0]:=F_BIGTYPE(pData:=ADR(r32Array),cbLen:=SIZEOF(r32Array)); MEMCPY(ADR(r32ArrayBuffer),ADR(r32Array),SIZEOF(r32Array)); MyComplexT_ARG_Arrays[1]:=F_BIGTYPE(pData:=ADR(dutVariable),cbLen:=SIZEOF(dutVariable)); MEMCPY(ADR(dutVaraibleBuffer),ADR(dutVariable),SIZEOF(dutVaraibleBuffer)); MyComplexT_ARG_Arrays[2]:=F_BIGTYPE(pData:=ADR(dutArray),cbLen:=SIZEOF(dutArray)); MEMCPY(ADR(dutArrayBuffer),ADR(dutArray),SIZEOF(dutArray)); MyComplexT_ARG_Arrays[3]:=F_BIGTYPE(pData:=ADR(myString),cbLen:=SIZEOF(myString)); MEMCPY(ADR(myStringBuffer),ADR(myString),SIZEOF(myString)); ComapreResult[0]:=MEMCMP(ADR(r32Array),ADR(r32ArrayBuffer),SIZEOF(r32Array)); ComapreResult[1]:=MEMCMP(ADR(dutVaraibleBuffer),ADR(dutVaraibleBuffer),SIZEOF(dutVaraibleBuffer)); ComapreResult[2]:=MEMCMP(ADR(dutArrayBuffer),ADR(dutArray),SIZEOF(dutArray)); ComapreResult[3]:=MEMCMP(ADR(myStringBuffer),ADR(myString),SIZEOF(myString)); |
Result
Done!MEMCMP関数を使用しT_ARG配列の各Elementsの現在値と比べ、0であれば同等の値が入っていると確認できます。