🐷

CODESYS Example Projectを調べてみた #3

2024/11/24に公開

Event Managerを学ぶ

今回は、Event Managerについて調査した結果をまとめます。このExampleでは、システムイベントを受信・送信する例です。

セットアップ

これまでと同様に以下の手順でセットアップできます。

  1. プロジェクトコードをダウンロード
  2. EventManager.projectをダブルクリックして、SDKのバージョンに合わせて環境設定
  3. DeviceをUpdate

Example Projectの解説

Example Projectには、以下のファイルが存在します。
Devices

プロジェクトの説明によると、関数やファンクションブロックをコールバック関数として登録する仕組みが示されています。この登録はFB_Initメソッド内で行われ、外部リソースの接続や動的メモリの確保などの初期化処理を実装する際に使用されます。FB_initの詳細は、CODESYS Online Helpを参照してください。

1. Main(PRG)

Mainプログラムでは、以下のようにコールバック関数型の変数を定義し、EventPostByEventで定期的にLogoutEventをトリガーしています。

Main(PRG)
PROGRAM Main
VAR
	dwAlive 			: DWORD; (* COunter *)
	Result				: RTS_IEC_RESULT;
	
	EventFB 			: EventCallbackFB;
	EventFunctionFB 	: EventCallbackFunctionFB;
	EventParam			: EventParam; (* Parameter for the posted event *)
END_VAR

dwAlive := dwAlive + 1;

(* Set parameter for the event to post*)
EventParam.CmpIdProvider := CMPID_CmpApp;
EventParam.EventId := EVT_Logout;
EventParam.usVersion := 1;
EventParam.usParamId := 1;

(* Send LogoutEvent --> Eventcounter will increment by one.*)
IF dwAlive MOD 500 = 0 THEN
	Result := EventPostByEvent(EVT_Logout, CMPID_CmpApp, ADR(EventParam));
END_IF

プログラム処理部分では、EventParamに値を設定しています。CMPID_CmpApp, EVT_Logoutは、CmpAppライブラリで標準定義されているEventIDです。

(ライブラリ Descriptionより)このライブラリは、ランタイムシステムのアプリケーションマネージャへのアクセスを提供する。すべての実行中アプリケーションとアプリケーションマネージャのすべてのイベントにアクセス可能。

EventPostByEventは、トリガーイベント、イベントに渡すパラメータ、およびパラメータのサイズを引数として受け取り、イベントを発生させるための関数です。イベントには、RTS_IEC_EVENT型が使用されます。

EVT_Logoutは標準イベントの1つで、ユーザーがCODESYSからログアウトしたことを通知します。このほかにも複数のEventIDが定義されており、詳細はCODESYS Online Helpで確認できます。

アプリケーションを実行すると、20ms × 500 = 10秒ごとにEventCountがインクリメントされます。また、開発環境でアプリケーションをStartまたはStopすることでもカウントが増加し、これらの操作がイベントとして拾われていることがわかります。

2. EventCallbackFB(FB)

EventCallbackFBでは、ICmpEventCallbackインターフェースを実装しています。このインターフェースを使うことで、イベント発生時にカスタム処理を定義できます。

EventCallbackFB(FB)
FUNCTION_BLOCK EventCallbackFB IMPLEMENTS ICmpEventCallback
VAR_INPUT
END_VAR
VAR_OUTPUT
END_VAR
VAR
	hEventStart			: RTS_IEC_HANDLE;
	hEventStop 			: RTS_IEC_HANDLE;
	hInterfaceStart 	: RTS_IEC_HANDLE;
	hInterfaceStop 	: RTS_IEC_HANDLE;
	ItfEventCallback 	: ICmpEventCallback;	
	Result 				: UDINT;	
END_VAR

ICmpEventCallbackは、イベントが発生した際に、ICmpEventCallbackインターフェースを実装したオブジェクトがそのイベントを受け取り、処理を行う仕組みを提供します。

この例では、コールバックメソッドとしてEventCallbackが実装されています。コールバックメソッドは、イベント識別ID、イベントに関するデータへのポインタ、パラメータのサイズを受け取ります。ただし、この例では、EventParam型を使用して、これらの情報を1つにまとめて受信しています。

EventCallback(METH)
METHOD EventCallback : UDINT
VAR_INPUT
	pEventParam : POINTER TO EventParam;
END_VAR
VAR
	pEvtParamIdStop : POINTER TO EVTPARAM_CmpAppStop;
END_VAR

(* For every registered event this functions is called, because the function block
   implements ICmpEventCallback. *)
GVL.diEventCount := GVL.diEventCount + 1;

(* Save the stop Reason in a global variable *)
IF pEventParam^.EventId = EVT_StopDone THEN
	pEvtParamIdStop := pEventParam^.pParameter;
	GVL.g_ulStopReason    := pEvtParamIdStop^.ulStopReason;
END_IF

処理部分では、EVT_StopDoneイベントが発生した場合、pEventParamとpEvtParamIdStopの内容がローカル変数とGVL変数へ値を書き込まれます。EVTPARAM_CmpAppStop構造体は、APPLICATOINへのポインタとDWORD型のulStopReaseon変数を持ちますが、どういった値が設定されるかはわかりませんでした。

Callbackの登録は、FB_Initの中でRventOpen, EventRegisterCallbackで行います。これらの関数は、SysCallback23 ライブラリに含まれています。

FB_Init(METH)
METHOD FB_Init : BOOL
VAR_INPUT
	bInitRetains: BOOL;
	bInCopyCode: BOOL;	
END_VAR
VAR
END_VAR

ItfEventCallback := THIS^;

(* Open an Event which listens to the start Event *)
hEventStart := EventOpen(EVT_StartDone, CMPID_CmpApp, Result);
(* Open an Event which listens to the stop Event *)
hEventStop := EventOpen(EVT_StopDone, CMPID_CmpApp, Result);

(* Register the Interface Callback functions to the event. *)
hInterfaceStart := EventRegisterCallback(hEventStart, ItfEventCallback, Result);	
hInterfaceStop := EventRegisterCallback(hEventStop, ItfEventCallback, Result);

FB_Exitの中ではCallbackの解除を行っています。

FB_Exit(METH)
METHOD FB_Exit : BOOL
VAR_INPUT
	bInCopyCode: BOOL;
END_VAR
VAR
END_VAR

(* Unregister and close both Events *)
EventUnregisterCallback(hEventStart, hInterfaceStart);
EventUnregisterCallback(hEventStop, hInterfaceStop);

Result := EventClose2(hEventStart);
Result := EventClose2(hEventStop);

3. EventCallbackFunction(FUNC)

EventCallbackFunctionFBで登録するコールバック関数が実装されています。

4. EventCallbackFunctionFB(FB)

EventCallbackFBでは自身のメソッドをコールバックに登録していましたが、こちらは上記のFUNCを登録しています。また、登録に使用する関数もEventOpen, EventRegisterCallbackFunctionであり、先ほどのものと微妙に異なります。

FB_Init
METHOD FB_Init : BOOL
VAR_INPUT
	bInitRetains: BOOL;
	bInCopyCode: BOOL;	
END_VAR
VAR
END_VAR

(* Open two events, one for start and one for stop *)
hEventLogin := EventOpen(EVT_Login, CMPID_CmpApp, Result);
hEventLogout := EventOpen(EVT_Logout, CMPID_CmpApp, Result);
(* Register a callback functino to an event *)
Result := EventRegisterCallbackFunction(hEventLogin, ADR(EventCallbackFunction));
Result := EventRegisterCallbackFunction(hEventLogout, ADR(EventCallbackFunction));

EventMagagerの機能を利用する際に関連する主なライブラリ

  1. CmpEventMgr(イベント管理の主要ライブラリ)
  2. CmpApp(アプリケーション関連のイベント管理)
  3. SysCallback23(コールバックの登録・解除)
  4. CmpEventMgr Interfaces(インターフェース定義)
  5. CmpLog(ログ管理)
  6. SysTypes2(型定義)
  7. CmpIecTask(タスク管理)

気づきと学び

  • EventManagerでは、標準イベントを使ったシンプルなトリガー処理から、カスタムコールバックの登録まで柔軟に実装可能。
  • イベントには、EventParam型を用いて必要なデータを渡せる。

まとめ

  • Eventに対応するCallbackを使うには、ICmpEventCallbackを実装し、EventRegisterCallbackでEventIDとCallback関数を対応づける。
  • IcmpEventCallback.EventCallbackは、EventParamポインタを受け取り、RTS_IEC_RESULTを返す。

Discussion