バインド済みイベント
説明
イベントのバインド(7.7の新機能)は、オブジェクトがイベントの生成とリッスンを行うための標準の方法です。これらのイベントはバインド済みイベント、または単にイベントと呼ばれます。
イベントプロバイダーの作成
イベントは必ず、オブジェクトと関連付けられています。イベントのバインドメカニズムはSimpleDataTypeクラスにより実装されているため、FlexSim階層のすべてのクラスが使用できます。オブジェクトにイベントを追加するには、次の手順を使用します。
- イベント名をクラスのメンバーとして、TreeNode*を追加します。名前は常に「on」で始まる必要があります。適切なイベント名の例は「onEntry」や「onArrival」です。VS 2013またはそれ以降を使用している場合、次に示すヘッダー初期化構文を使用します。
class MyObject : public FlexSimObject { public: TreeNode* onDoStuff = nullptr; void bindEvents() override; void doStuff(); ...
bindEvents()
メソッドをオーバーライドします。作成する各イベントノードについて、bindEventマクロを使用します。このマクロはイベントの名前を取得し、先頭の「on」を取り除きます。親クラスのイベントすべてを継承する場合は、親のbindEvents()
メソッドを呼び出します。void MyObject::bindEvents() { FlexSimObject::bindEvents(); // bind the base class events bindEvent(DoStuff); // bind the onDoStuff event }
- イベントを生成するには、
FIRE_SDT_EVENT()
マクロを使用します。このマクロは、起動すべきノードと、イベントに属するデータを引数として受け取ります。イベントが起動されるのは、そのイベントがリッスンされている場合のみです。リッスンされていない場合、FIRE_SDT_EVENT()
マクロは何の動作も行いません。void MyObject::doStuff() { double data; TreeNode* item; // ... do logic that sets the value of data and item FIRE_SDT_EVENT(onDoStuff, item, data); }
イベントのリッスン
バインド済みイベントをリッスンするには、最初にFlexSimEventクラスを継承してから、execute()
メソッドをオーバーライドする必要があります。これにより、リスナークラスが作成されます。
class OnDoStuffListener : public FlexSimEvent { public: void execute() override { CallPoint* callPoint = getListenerCallPoint(); TreeNode* item = param(1); double data = param(2); // ... define additional behavior for when onDoStuff is fired } };
getListenerCallPoint()
メソッドを使用して、FIRE_SDT_EVENT()
マクロに渡すパラメータを取得できることに注意してください。結果として得られるポインターにはcallPoint
という名前を付ける必要があります。この名前でないとparam()
マクロは動作しません。
次に、リッスンする対象のオブジェクトでイベントをアサートする必要があります。
MyObject* myObject = objectNode->objectAs(MyObject); TreeNode* onDoStuffNode = myObject->assertEvent("OnDoStuff");
assertEvent()
メソッドはTreeNode*
を返すことに注意してください。このノードにサブノードを追加し、リスナークラスを加える必要があります。また、両方のノードでswitch_activelistenersがオンになっていることを確認する必要があります。
TreeNode* listenerNode = nodeinsertinto(onDoStuffNode); nodeaddcouplingdata(listenerNode, new OnDoStuffListener(), 1); switch_activelisteners(onDoStuffNode, 1); switch_activelisteners(listenerNode, 1);
onDoStuff
イベントが起動したとき、execute()
メソッド(新しいOnDoStuffListener
に含まれる)も実行されます。
イベントの選択
イベントをアサートして、イベントの名前を渡すと、オブジェクトのイベントすべての中から指定された名前のイベントが検索されます。ランタイムに多くのイベントを追加する場合は、select
メカニズムを使用します。次の動作を1回だけ、通常はリセット時に行います。
sdt->enumerateEvents(destNode, true); // this will populate the EventBinding with all the needed data EventBinding* binding = destNode->objectAs(EventBinding); binding->select("OnDoStuff"); // this selects the given event
その後で実行時に、次のような動作を実行します。
treenode eventNode = sdt->assertEvent(binding);
この方法でイベントを選択すると、検索は行われません。同じバインドオブジェクトを繰り返し使用し、目的のイベントノードを得ることができます。実際には、オブジェクトのタイプが同じなら、同じバインドを別のオブジェクトにも使用できます(目的のイベントを選択して)。
treenode eventNode1 = sdt1->assertEvent(binding); treenode eventNode2 = sdt2->assertEvent(binding);
前の例に示した呼び出しはそれぞれ、各オブジェクトのonDoStuffイベントノードを返します。
イベントの列挙
enumerateEvents()
メソッドを使用して、オブジェクトに対して、そのオブジェクトのイベントを照会できます。このメソッドのパラメータはツリーノードとブール値です。ツリーノードはデスティネーションで、そのオブジェクトが生成可能なすべてのイベントについてのデータが入力されます。ブール値が真なら、データはノードのEventBindingオブジェクトに格納されます。それ以外の場合、データはデスティネーションノードのツリーノードテーブルに格納されます。イベントが列挙されたら、次の方法ですべてのイベントに対して操作を繰り返し実行できます。
// as an event binding node EventBinding* binding = destNode->objectAs(EventBinding); for (EventBindingEntry& entry : binding->events) { ... // as a table (recommended) for (int i = 1; i <= content(destNode); i++) { ...
各エントリには、イベントの名前とイベントフラグが格納されます。イベントフラグはビットマスクで、イベントについての情報が含まれます。これには、イベントにいくつの要件が存在するかの情報も含まれています(イベントが中継される場合)。
int flags = get(node("flags", rank(destNode, i))); int numReqs = sdt->getNumRequirementsFromFlags(flags); // numReqs will be 0, 1, 2, or 3
中継されるイベント
オブジェクトが動的な数のサブオブジェクトを管理する場合があります。これらのサブオブジェクトにイベントが存在する場合、親オブジェクト経由でそれらのイベントにアクセス可能です。このためには、中継されるイベントを次のようにバインドします。
void MyObject::bindEvents() { bindRelayedClassEvents<SubObjectType>("", EVENT_1_REQUIREMENT, &MyObject::resolver); }
bindRelayedClassEvents<>()
関数は、テンプレートを使用する関数です。テンプレートパラメータは、親により管理されるクラスのタイプです。サブオブジェクトには独自のbindEvents()
メソッドが必要で、このメソッドは追加の中継されるイベントにバインドできません。
この関数の引数は次のとおりです。
- プレフィックス - char*で、中継されるイベントのプレフィックスを指定します。サブオブジェクトに「OnArrival」イベントが存在し、それを所有するオブジェクトがプレフィックス「Sub」を追加した場合、そのイベントは「OnSubArrival」として親により列挙されます。
- 要件フラグ - ビットマスクで、このイベントを取得するためにいくつの要件値が必要かを指定します。通常は要件値が1つだけなので、このパラメータは
EVENT_1_REQUIREMENT
にします。 - リゾルバ - 要件値を使用して、適切なサブオブジェクトを返す関数。この引数で受け付けられる関数は3種類のみです。
これらはすべてツリーノードを返し、1から3までのバリアントを受け付けることに注意してください。返されるツリーノードは、目的のイベントを持つオブジェクトを保持している必要があります。関数は、その入力を使用してオブジェクトを取得します。treenode resolver1(const Variant& p1); treenode resolver2(const Variant& p1, const Variant& p2); treenode resolver3(const Variant& p1, const Variant& p2, const Variant& p3);
統計をイベントとしてバインドする
バインドされた統計のメカニズムをオブジェクトで使用する場合、それらの統計のそれぞれをイベントとしてバインドし、統計の変化をリッスンできます。そのためには、bindStatisticsAsEvents()
を、オブジェクトのbindEvents()
メソッドの一部として呼び出します。これにより、バインドされたそれぞれの統計について、オブジェクトにOn[Statistic]Changeイベントが自動的に追加されます。
EventInfoオブジェクト
オブジェクトをFlexSimライブラリに追加する場合、オブジェクトのeventfunctionsノードにeventinfoノードも追加します。このノード内には、バインドされる各イベントについてオブジェクトが必要です。これらの各オブジェクトにはvariables属性が必要で、その属性内にはparamsとrequirementsの2つのサブノードが必要です。
params
paramsノードは、そのイベントのFIRE_SDT_EVENT()
マクロに渡される各パラメータの名前とタイプを示すテーブルです。
例として、リストのOnPushイベントのeventinfoオブジェクトを次の図に示します。
このノードは次の条件を満たしていることに注意してください。
- オブジェクトデータが存在する。
- イベントと同じ名前である。
- variables属性が存在する。
- 変数としてparamsノードが存在する。
このイベントは中継されるイベントではないため、requirementsはありません。paramsノードを右クリックしてテーブルとして調べると、次の図に示すような表が示されます。
この表から、OnPushイベントが起動するときは2つのパラメータにアクセス可能なことが分かります。リストにプッシュされた値(バリアント)と、値がプッシュされた先のパーティションID(同様にバリアント)です。
requirements
requirementsノードは、イベントを正しくリッスンするために必要な要件を示すテーブルです。ほとんどのイベントは中継されるイベントではないため、requirementsノードは必要ありません。しかし、オブジェクトに中継されるイベントがある場合、それらのイベントにはrequirementsノードが必要です。
たとえば、リストにはOnPartitionContentChangeイベントがあります。このイベントは、パーティションのコンテンツが変更されたときに発生します。しかし、このイベントをリッスンするには、パーティションIDを指定し、そのパーティションのイベントをリッスンする必要があります。これは、リストのOnPartitionContentChange eventinfoノードに反映されています。
requirementsノードをテーブルとして調べると、次の表が示されます。
この表から、このイベントをリッスンするにはパーティションIDの指定が必要なことが分かります。この値は引数としてassertEvent()
に指定します。この関数には最大3つの要件値を指定できます。この関数は、これらの要件値をbindRelayedClassEvents<>()
で指定されたリゾルバに送り、名前付き(または選択済み)イベントを持つオブジェクトが返されます。