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

今回の記事はBeckhoff TwinCAT3のANY Typeについて説明します。ANY TYPEのコンセント・使用方法を理解することにより、可変メモリのプログラム、そしてFlexibleなプログラムを目指すのが第1 Stepではないかな?と思っています。

さ、始めましょう。

ANY?

Beckhoff TwinCATは関数、メソッド、ファンクションブロック(Build4026以降)を実装する場合、入力(VAR_INPUT)を汎用IEC日付型ANYまたはANY_<type>の変数として宣言できます。なので、呼び出しされたパラメータがData Typeによって異なる制御を実装できるようになりました。Runtime中にANYの構造体を介して、転送された値・Data Type・サイズを求めることが可能です。

実際、コンパイラは内部で入力変数の型を構造体に置き換え、値は直接転送なく、

代わりに実際の値へのポインタが転送されます。。したがって、パラメータのData Tyoeによってそれぞれ異なるデータ型を持つ制御ができます。

Internal data structure

TwinCATプログラムをコンパイルする時、ANYデータ型の入力変数は、内部で以下の構造体に置き換えられます。構造体の要素は、実行時に実際の呼び出しパラメータに割り当てられる。

TYPE AnyType :
STRUCT
      // the type of the actual parameter
  typeclass  : __SYSTEM.TYPE_CLASS ;
      // the pointer to the actual parameter
  pvalue     : POINTER TO BYTE;
      // the size of the data, to which the pointer points
  diSize     : DINT;
END_STRUCT
END_TYPE
VariablesData TypeDescription
typeclass__SYSTEM.TYPE_CLASSパラメータのData Type
pvaluePOINTER TO BYTEパラメータのPointer
diSizeDINTパラメータのメモリサイズ

Example1

最初のExampleではANY Typeを使用し、異なるData Typeのパラメータを関数に渡し、そのパラメータのData TypeをUDINTの形でReturnします。

Program

FC_GetVarType

注目するのはmyAnyDataのData TypeはAnyです。

FUNCTION FC_GetVarType : UDINT
VAR_INPUT
myAnyData :ANY;
END_VAR

FC_GetVarType:=myAnyData.TypeClass;

MAIN

Functionを何回も繰り返して呼び出して、異なる変数をパラメータとして渡します。

VAR
wStringVar:STRING;
StringVar:WSTRING;
BoolVar:BOOL;
ByteVar:BYTE;
WordVar:WORD;
DWordVar:DWORD;
LWordVar:LWORD;
USIntVar:USINT;
IntVar:INT;
RealVar:REAL;
myReturnValues:ARRAY[0..99]OF UDINT;
END_VAR


myReturnValues[0]:=FC_GetVarType(myAnyData:=wStringVar);
myReturnValues[1]:=FC_GetVarType(myAnyData:=StringVar);
myReturnValues[2]:=FC_GetVarType(myAnyData:=BoolVar);
myReturnValues[3]:=FC_GetVarType(myAnyData:=ByteVar);
myReturnValues[4]:=FC_GetVarType(myAnyData:=WordVar);
myReturnValues[5]:=FC_GetVarType(myAnyData:=DWordVar);
myReturnValues[6]:=FC_GetVarType(myAnyData:=LWordVar);
myReturnValues[7]:=FC_GetVarType(myAnyData:=USIntVar);
myReturnValues[8]:=FC_GetVarType(myAnyData:=IntVar);
myReturnValues[9]:=FC_GetVarType(myAnyData:=RealVar);

Result

FC_GetVarTypeの内部をみてみましょう。myAnyDataの構造体では先程書いてた通り、TYPE_CLASS・diSize・pValueもあります。

Done!パラメータのData TypeによってReturn値が違います。

この数字の意味を調べるため、ReferencesからBase_InterfacesのLibraryをImportします。

TypeClassには各Data Typeを分別するためのENUMがあります。次のExampleはこのENUMを使ってみます。

Example2

Example2では先程紹介したTypeClassとCASE文を使用しパラメータのData Typeを文字列としてReturnします。

Program

FC_GetVarTpeAsString

新しいFunctionを作成し、パラメータはANY Typeします。

FUNCTION FC_GetVarTpeAsString : String
VAR_INPUT
myAnyData :ANY;
END_VAR

CASE UDINT_TO_INT(myAnyData.TypeClass) OF

IBaseLibrary.TypeClass.TYPE_BOOL:
FC_GetVarTpeAsString:=’Bool’;
IBaseLibrary.TypeClass.TYPE_BYTE:
FC_GetVarTpeAsString:=’Byte’;
IBaseLibrary.TypeClass.TYPE_REAL:
FC_GetVarTpeAsString:=’Real’;
IBaseLibrary.TypeClass.TYPE_INT:
FC_GetVarTpeAsString:=’Int’;
IBaseLibrary.TypeClass.TYPE_WORD:
FC_GetVarTpeAsString:=’Word’;
IBaseLibrary.TypeClass.TYPE_DWORD:
FC_GetVarTpeAsString:=’DWord’;
IBaseLibrary.TypeClass.TYPE_LWORD:
FC_GetVarTpeAsString:=’LWord’;
IBaseLibrary.TypeClass.TYPE_STRING:
FC_GetVarTpeAsString:=’String’;
IBaseLibrary.TypeClass.TYPE_WSTRING:
FC_GetVarTpeAsString:=’WString’;

ELSE
FC_GetVarTpeAsString:=’Unknown’;
END_CASE

MAIN

Example1とあまり変わりなく、呼び出しされた関数が違うだけです。

PROGRAM MAIN
VAR
wStringVar:STRING;
StringVar:WSTRING;
BoolVar:BOOL;
ByteVar:BYTE;
WordVar:WORD;
DWordVar:DWORD;
LWordVar:LWORD;
USIntVar:USINT;
IntVar:INT;
RealVar:REAL;
myReturnValues:ARRAY[0..99]OF UDINT;
myReturnStrings:ARRAY[0..99]OF STRING;
END_VAR


myReturnStrings[0]:=FC_GetVarTpeAsString(myAnyData:=BoolVar);
myReturnStrings[1]:=FC_GetVarTpeAsString(myAnyData:=StringVar);
myReturnStrings[2]:=FC_GetVarTpeAsString(myAnyData:=BoolVar);
myReturnStrings[3]:=FC_GetVarTpeAsString(myAnyData:=ByteVar);
myReturnStrings[4]:=FC_GetVarTpeAsString(myAnyData:=WordVar);
myReturnStrings[5]:=FC_GetVarTpeAsString(myAnyData:=DWordVar);
myReturnStrings[6]:=FC_GetVarTpeAsString(myAnyData:=LWordVar);
myReturnStrings[7]:=FC_GetVarTpeAsString(myAnyData:=USIntVar);
myReturnStrings[8]:=FC_GetVarTpeAsString(myAnyData:=IntVar);
myReturnStrings[9]:=FC_GetVarTpeAsString(myAnyData:=RealVar);

Result

Done!Data Typeに沿って文字列がReturnされました。

Example3

ANY TYPEは実際すごく大きな枠で、ANY_BIT・ANY_DATE・ANY_NUM・ANY_STRINGに分かれ、ANY_NUMは更にANY_REALとANY_INTがあります。

例えばANYの代わりにANY_NUMを宣言すると、Userが関数などに渡せるパラメータを実数・整数に制限できます。

Program

実際にプログラムを作ってみましょう。

FC_GetVarType_ANYNUM

関数のプログラムより、myAnyDataはANY_NUMとして定義されています。つまりこの関数は関数に渡せるパラメータは実数・整数だけになります。

FUNCTION FC_GetVarType_ANYNUM : bool
VAR_INPUT
myAnyData :ANY_NUM;
END_VAR

//Do something

MAIN

MAINプログラムでは整数だけではなく、文字列とBoolの変数も関数に渡してみます。

myReturnBool[0]:=FC_GetVarType_ANYNUM(myAnyData:=USIntVar);
myReturnBool[1]:=FC_GetVarType_ANYNUM(myAnyData:=BoolVar);
myReturnBool[2]:=FC_GetVarType_ANYNUM(myAnyData:=DWordVar);
myReturnBool[3]:=FC_GetVarType_ANYNUM(myAnyData:=StringVar);

Result

プログラムをコンパイルするとエラーが出で、BoolVarとStringVarが赤い線がついています。

Errro Listを見てみましょう。Cannot convert type ‘BOOL’ to type “ANY_NUM’のエラーメッセージがあり、コンパイラはパラメータのData TypeをANY_NUMに宣言することによって、ある程度はCheckしてくれます。

Example4

今度は関数に2つのANY TYPEパラメータを渡し、Data Size・Data Typeと現在値を比べてみます。

Program

FC_CompareDataInAnyType

こちらの関数は2つのANYパラメータとして定義します。

  • もしData Sizeが異なるなら、Bit0=Trueします。
  • もしData Typeが異なるなら、Bit1=Trueします。
  • もし現在値が異なるなら、比較のFor Loopから離れ、Bit2=Trueします。
FUNCTION FC_CompareDataInAnyType : WORD
VAR_INPUT
myAnyData1 :ANY;
MyAnyData2  :ANY;
END_VAR
VAR
i:DINT;
END_VAR

FC_CompareDataInAnyType:=0;

IF myAnyData1.diSize <> MyAnyData2.diSize THEN
FC_CompareDataInAnyType.0:=TRUE;
END_IF

IF myAnyData1.TypeClass <> MyAnyData2.TypeClass THEN
FC_CompareDataInAnyType.1:=TRUE;
END_IF


FOR i:=0 TO myAnyData1.diSize-1 DO
IF myAnyData1.pValue[i] <> MyAnyData2.pValue[i] THEN
FC_CompareDataInAnyType.2:=TRUE;
RETURN;
END_IF
END_FOR


VAR
compareInt1,compareInt2:INT;
compareReal1,compareReal2:REAL;
compareString1:STRING(50);
compareString2:STRING(20);
myReturnWord:ARRAY[0..99]OF WORD;
END_VAR

Result

もし二つのパラメータが同じData Type、なおかつ現在値が同等であれば、Retrun値が0になります。

二つのパラメータは同じData Typeですが、現在値が異なりますので、Return値が4(Bit2=True)になります。

2つのパラメータは異なるData Typeであれば、Return値が3(Bit0,Bit1=True)になります。

Example5

最後のExampleは関数が異なるData Typeによって制御を分けることを試してみましょう。

Program

今回の関数は結果としてはパラメータの現在値をDINT Data Typeとして変換するだけですが、理論さえ分かれば、複雑な制御を作れます。

FC_TransferDataFromAnyToDINT

注目するのはVARエリアで3つのPOINTER変数が定義され、CASE文からANY TYPEのData Typeを分別し異なるPointerにアクセスし、現在値をDINT Formatに変換します。

もし該当するData TypeがReal・LReal・DWORD以外のものであれば、DataTypeInValidがTrueになります。

FUNCTION FC_TransferDataFromAnyToDINT : DINT
VAR_INPUT
myAnyData :ANY;
END_VAR

VAR_OUTPUT
DataTypeInValid:BOOL;
END_VAR
VAR
pReal :POINTER TO REAL;
pLReal :POINTER TO LREAL;
pDWORD :POINTER TO DWORD;
END_VAR

DataTypeInValid:=False;

CASE UDINT_TO_INT(myAnyData.TypeClass) OF

IBaseLibrary.TypeClass.TYPE_REAL:
pReal:=myAnyData.pValue;
FC_TransferDataFromAnyToDINT:=REAL_TO_DINT(pReal^);
IBaseLibrary.TypeClass.TYPE_LREAL:
pLReal:=myAnyData.pValue;
FC_TransferDataFromAnyToDINT:=LREAL_TO_DINT(pLReal^);
IBaseLibrary.TypeClass.TYPE_DWORD:
pDWORD:=myAnyData.pValue;
FC_TransferDataFromAnyToDINT:=REAL_TO_DINT(pDWORD^);
ELSE
DataTypeInValid:=TRUE;
END_CASE

Result

32Bit実数、64Bit実数、32BitWordをパラメータとして関数に渡しても正常に変換されます。

ですが、Bool Typeの変数を渡してみるとエラーに代わります。

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

シェアする

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

フォローする