前回はMethod・Property・とFunction Block自体のことを説明しました。今回はFunction Blockはどういうものなのか、実際あなたのプロジェクトにどんな役割あるのかを自分なりの考えを説明や、最後にInterfaceという利器を紹介したいと思います。
Function Blockは必要なの?
正直にいいますと、この議論はおそらくいくらでも終わらないでしょう。Function Blockでプロジェクトを作成するの中に各部分、例えばサボー・Blower・シリンダーなどのことを一つの”Object”として扱い、そのObjectの特徴や動作を抜き出して”抽像化”するです。
ここでシリンダーを例として話ししましょう。
シリンダーを制御するには、みんなさんなにが思い付きますか?
工作位置と原点のSensor入力とそのシリンダーの出力、動作できるかどうかのインタロック・シリンダー状態を判断するFault・Warning。
(もちろんまだたくさんありますが、ここでとりあえず簡単な特徴だけ洗い出します)
そうすると、下図のような絵が書けると思いますね。それはシリンダーというObjectができあがりです。
次みんなさんはどうやってこのシリンダーの制御プログラムを作成するでしょうか。普段国産のPLCになると、おそらくこのようではないでしょうか。
シリンダーA:D1000~D1009
シリンダーB:D1010~D1019
シリンダーC:D1020~D1029
のように、シリンダーからDなどのメモリを振り分けて、そしてさらに
D1000.0ならInBasePosition、D1000.1ならInWorkPosition、D1000.2ならGoBasePosition…のようにわけるではないでしょうか。
Function Blockになると、やってることもそんなに変わりません。変わったのはD1000、D2000のような”Global Address”を使わなくなります。
例えば、
if M0.0 then //Some Trigger Condition D1000.2:=True; //Go BasePosition End_if; |
の代わりに、全て名前がついてるLocal変数やINPUT/OUTPUT変数に変わります。
If inbPBGoBasePosition then GoBasePosition:=True; End_If; |
国産のPLCでプログラムを作成するときみんなさんはよくPOUを作りますね。P200,P201,P202..のような”CALL”をたくさん作ってプログラムの機能こととかで分けますね。Function Blockも同じです。ですが、自分のFunction Blockを作成しただけで機能しませんね。そのFunction Blockを”呼び出し”、そして”Instance”、つまりメモリを与える必要があります。自分がよくハンコと似てると説明します。
つまり、ハンコ自体はFunction BlockやそのObjectです。そしてあなたがそのはんこを紙にぎゅうーと押して押した赤い印はそのInstanceです。ハンコは1つだけですが、何回を紙上で押すのはあなたの自由です。
もしあなたの機械には2つのシリンダーがあり、PusherとLiferがあるとしましょう。そこでまずPOU(紙)からFB_CyclinderというFunction Block(ハンコ)を二回押して、名前がSV_Pusher・SV_Liferを定義すればOKです。
VAR SV_Pusher :FB_Cyclinder; SV_Lifter :FB_Cyclinder; END_VAR |
次やるのはその2つのInstanceを呼び出すればよいです。
SV_Pusher(); SV_Lifer(); |
そうするだけで、SV_PusherとSV_Liferは同じFunction Block(ハンコ)から押されたものですが、メモリ領域がまったく違いますし、かぶることがありません。そして将来にもしシリンダーの制御が変更することがあれば(もしではなく絶対)FB_Cyclinderの中身だけを修正すれば、そのFunction Block(ハンコ)から定義されたInstanceすべて自動更新されます。そこで修正コストも下がりますしメモリ領域のかぶりも心配いりません。
メモリ領域のかぶりがないので、”プログラムの標準化”にもなります。”Block”を他のプロジェクトに展開するときにこのアドレス使ってるのかな?などの心配もないですね。
次に、例えばいまの機械にPusherとLifterだけですが、もう1つのシリンダー SV_LifterExitがあるとしましょう。そのSV_LifterExitは自分の温度センサーと圧力センサーがついています。ですが、いまのFB_Cyclinderを修正したくない。そのときに出てきたコンセプトは前回招待してたEXTENDSの機能を使えばよいだと思います。
そうすると、FB_Cyclinder_DiagnosticsがFB_Cyclinderの特徴を持ちながら自分だけの”特徴”をさらに拡張されます。それはIEC61131-1のOOP ProgrammingのExtendsの威力です。
Interface
Blogの始まりではInterfaceがFunction Blockの利器だと言いました。そのInterfaceはIEC61131の中に拡張されるもので。InterfaceはFunction Blockをなにやるかだけを定義します。どうやるかは定義しません。
このInterfaceはあなたのFunction Blockの”Blue Print”といってもよいだと思い、例えばみんなさん将棋遊んだことありますか?
すべての駒にもInterfaceがある!と想像してみてください。駒が移動ができますよね。ですが、駒の種類によって移動のルールが異なる。
なので駒のInterfaceにはmove()という機能が定義されています。つまり、駒ObjectをはMoveできますよ~でもどんな風にMoveするか知りませんー
そしてKing(王将)やSolders(歩兵)などのFunction Blockを作るとき、そのInterfaceを”Implement”します。
そうするとKing・Soldersには自分のMove()機能があり、そのときこのどうMoveするかを作るんです。
まとめになりますと、Intefaceは:
- なにをやるかだけを定義
- Function BlockのBlueprint
- 誰にもそのInterface にImplementsれたFunction Blockをプログラムすると、そういう機能が必ずあり実装しますの約束ことでも言える
ですね。
どう使うの?
例えば先定義したFB_Cyclinderなら、まずこのようにInterfaceを定義できますね。
中身にどうやってOvertimeのアラームを定義するの?WorkPositionに出力するときのInterlockはどうする?などの細かい制御が考えません。
自分ならよくまず絵を書きます。実際にTwinCAT XAEを立ち上げてプログラミングする前に。
必ず使わないとだめ?
まずそんなことは絶対ありません。ですが、Interface を使うことによって複数のメンバーでプロジェクト作業するとき、お互いに最低限の干渉だけになります。プログラマーはIntefaceだけに注目し、自分の機能だけ実装すればよいですがら。
あと設計する段階で機能の洗い出しなどにもやつに立ちますね。
Extendsとなにが違うの?
Extendsとは少し似てるじゃない?とみんなさん思うかもしれませんが。確かに、Extendsは元の親Function Blockからがある”もの”を持ちながら自分だけの属性をさらに拡張できます。でも、Extendsは一回だけです。つまり、複数の親Function Blockができません。
その代わりに、Function Blockが複数のInterfaceからImplementすることが可能です。
例えば、先のFB_CyclinderのObjectsになりますと、もしFB_Cyclinder_DiagnosticsがOn/Off/ErrorなどのStatus記録が必要であれば、CyclinderのInterfaceとEventLoggerのInterfaceを両方同時にImplementすることが可能ですね。
Interface作成Example
ここであなたがLight TowerのInterfaceを作成したいと考えとしましょう。
LightTowerは点灯するかどうかがあり、点灯する色もあり、Buzzerもついてます。
まずはPLC>POUs>Add>Interfaceします。
次はInterfaceの名前を入れて、Openします。
そうするとI_LightTowerができあがります。
I_LightTowerに右クリック>Methodします。
先ON/OFFがあると言いましたね。NameはMethod_ONOFFだと定義します。
そしてそのMethodはBoolのコマンドを受けます。それだけで定義終わります。
前も書いてた通り、Interfaceは”なに”やるかだけを定義し、”どう”やるかはやらない。
それで3つのMethodが作成できました。
Function BlockにImplement
Interface作成した所、次はそのInterfaceをFunction BlockにImplementします。
POUs>Add>POUします。
例えばいまOmron製のLightTowerがあるとしましょう。
NameはLightTower_Omronします。
Function Blockを選び、ImplementsのCheckboxを入れて、…のボタンをクリックします。
Input Assistantから先作ったI_LightTowerを選び、OK。
それでOK。
Solution Explorerから新しいFunction Block作った同時に、先I_LightTower定義した3つのMethodも一緒に作られました。
Method_Buzzerを開くと{warning ‘add method implementation ‘}がありますね。
つまりInterfaceからこのMethodがImplementされただけで、プログラムなにもありませんよ、と。
最後に、自分のCodeをいれてくださいね。
ここからは”なにやる”を定義するではなく、”どうやる”の時間ですからね。
最後に
先のLampTowerは簡単なExmapleだけになりますが、FB_LightTower_OmronとFB_LightTower_SIEMENSとFB_LightTower_SICKのように他のメーカーのLightTowerもあり、Profinetで繋がったり、EtherCATで繋がったりそれぞれの通信Protocolも違う可能性もあります。Interface定義するときはそれらのことも一切きにしません。
”同じなもの持ち、中身が違う”ですね。
Function Blockの話はあと一回書こうかな?と考えています。(ま、いくらでも書けますよ~)一気にOOPコンセプトのすべてを導入するのは難しいと思いますし、なれない人にとってはなおさらですね。
おすすめしたいのはゆっくりで少しずつ変わっていくのはBestではないか?と思っています。もちとんOOPコンセプト使わず、そのままFunction Blockだけ使ってもよいですし、そもそもFunction Blockも使わないっていっても構いません。
プログラムは自由自在です。
一番言いたかったのは、このコンセプトを知ることが大事だと思います。Beckhoffだけではなく、Codesys、SiemensのSimotionにも同じのようなプログラミングスタイルありますし、一回勉強したら他のPLCにも使える!のはメリットではないかな?と思っています。
あと、やっばりInterface・Propertyなどのツールは1つのObjectを細かく分けることでき、プログラムの修正は他のUnitとの干渉には減るじゃないかな?と思っていますね。
では、お疲れ様っすー