Beckhoff#TwinCAT T_ARG Typeを使ってみよう

今回の記事ではANYの次に、T_ARG Data Typeを紹介します。T_Argは、C#のジェネリックやC++のテンプレートには対応していませんが、IEC 61131-3のジェネリック関数の開発には対応しています。それによって、異なるデータ型やデータ構造に対して同じ関数を使用できるような設計が可能になります。

さ、始めよう。

Reference Link

Beckhoff#TwinCAT Any Typeを使ってみよう

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であれば同等の値が入っていると確認できます。

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

シェアする

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

フォローする