タスクシーケンスの概念
カスタム構築タスクシーケンス
3つの簡単なコマンドを使用してカスタムタスクシーケンスを作成できます。
まず、TaskSequence.create()を使用してタスクシーケンスを作成します。その後、連続したaddTask()コマンドでタスクをタスクシーケンスに追加します。最後に、dispatch()でタスクシーケンスを送付します。
次の例では、「ステーション」として参照されるオブジェクトにフォークリフトが移動し、「アイテム」として参照されるフローアイテムをロードするように指示しています。
TaskSequence ts = TaskSequence.create(forklift, 0, 0);
ts.addTask(TASKTYPE_TRAVEL, station);
ts.addTask(TASKTYPE_LOAD, item, station, 2);
ts.dispatch();
TaskSequence.create()コマンドは3つのパラメータを取ります。最初のパラメータは、タスクシーケンスを処理するオブジェクトです。これはディスパッチャーまたはタスク実行者オブジェクトになります。2番目と3番目のパラメータは数値であり、タスクシーケンスの優先度とプリエンプトの値をそれぞれ指定します。このコマンドは、作成されたタスクシーケンスの参照を返します。
addTaskメソッドは、タスクシーケンスの最後にタスクを追加します。追加するタスクにはそれぞれ、いくつかの値が関連付けられています。まず、タイプの値があり、タスクのタイプを定義します。また、タスクの2つの関連オブジェクトの参照が含まれます。involved1とinvolved2と呼ばれています。これらの関連オブジェクトとその意味は、タスクタイプに依存します。両方の関連するパラメータを必要とし、意味が与えられるタスクタイプがある一方で、関連オブジェクトを使用しないタスクタイプもあります。関連オブジェクトを1つ使用するタスクタイプもあれば、任意の関連オブジェクトを含むタスクタイプもあります。特定のタスクタイプの関連オブジェクトの意味については、タスクタイプのドキュメントを参照してください。タスクは追加の値を最大4つ持つことができます。var1、var2、var3、var4と呼ばれるタスク変数がそれです。ここでもその意味はタスクタイプに依存します。以下のロードタスクでは、var1が1として指定されています。ロードタスクの場合、これによりアイテムがステーションを離れる出力ポートが指定されます。
addTaskコマンドは、タスクの値を指定する複数のパラメータを取ります。最初のパラメータは、タスクが挿入されるタスクシーケンスの参照です。2番目のパラメータはタスクのタイプです。これは、タスクタイプが列挙されている一覧から選択できます。3番目のパラメータと4番目のパラメータは2つの関連オブジェクトを参照します。あるタスクタイプに対して特定の関連オブジェクトが使用されない場合または任意となる場合、単純にNULLをaddTaskメソッドに渡すことができます。あるいは、指定する必要がある数値変数がない場合、そのパラメータを除外できます。5番目から9番目のパラメータは任意であり、var1-var4を定義します。デフォルトでは、これらの値はゼロです。
タスクシーケンスのプリエンプト
タスクシーケンスにはすべて、プリエンプト値があります。プリエンプトを使用すると、より重要なオペレーションを実行するために、タスク実行者を現在のオペレーションから切り離します。たとえば、オペレーターAの最も重要な責務は機械を修理することです。しかし、修理するマシンがない場合は、モデル全体で材料を輸送する必要もあります。オペレーターAがフローアイテムをどこかで輸送している間に機械が故障した場合、オペレーターはそのときの作業を止め、運搬作業を完了せず、機械を修理します。これを行うために、プリエンプトするタスクシーケンスを使用し、オペレーターを現在のオペレーションから離します。
プリエンプトするタスクシーケンスを作成するには、TaskSequence.create()コマンドのpreemptパラメータにゼロ以外の値を指定します。
TaskSequence.create(operator, 0, PREEMPT_ONLY);
プリエンプト値として指定できる値は4つあります。これらの値は、プリエンプトされた元のタスクシーケンスをどのように処理するか、タスク実行者に指示します。
- 0 - PREEMPT_NOT - この値の場合、プリエンプトは行われません。
- 1 - PREEMPT_ONLY - タスクシーケンスにこの値が与えられている場合、タスク実行者は現在アクティブなタスクシーケンスをプリエンプトし、タスクシーケンスキューに戻してから、後で完了します。プリエンプトされたタスクシーケンスは、タスクシーケンスキューの先頭に自動で配置されます。その後、タスク実行者が元のタスクシーケンスに戻ったとき、そのタスクシーケンスにある現在のタスクは完了していないため、それを再び終わらせます。また、TASKTYPE_MILESTONEタスクを使用すれば、タスクシーケンスに戻ったときにやり直す一連のタスクを指定できます。このプリエンプト値が最もよく使用されます。
- 2 - PREEMPT_AND_ABORT_ACTIVE - タスクシーケンスにこの値が与えられている場合、タスク実行者は現在アクティブなタスクシーケンスを停止し、元のタスクシーケンスに戻ることがないように破棄します。
- 3 - PREEMPT_AND_ABORT_ALL - タスクシーケンスにこの値が与えられている場合、タスク実行者は現在アクティブなタスクシーケンスを停止し、破棄し、そのタスクシーケンスキュー内のすべてのタスクシーケンスを破棄します。
タスクシーケンスのプリエンプト値や優先度の値にクエリや変更を行うには、getpreempt()、setpreempt()、getpriority()、setpriority()といったコマンドを使用できます。これらのコマンドの詳細については、コマンドヘルパーを参照してください。
複数のプリエンプトするタスクシーケンス間の相互作用
タスク実行者が現在、プリエンプトするタスクシーケンスで作業している際に、やはりプリエンプトである新しいタスクシーケンスを受け取った場合、タスクシーケンスの優先度の値を使用し、実行するタスクシーケンスを決定します。新しいタスクシーケンスの優先度の値が、現在作業中のものより高い場合、タスク実行者は現在のタスクシーケンスをプリエンプトして新しいタスクシーケンスを実行します。新しいタスクシーケンスの優先度の値が現在作業中のタスクシーケンスの優先度と同じか、それより下の場合、タスク実行者はアクティブなタスクシーケンスをプリエンプトせず、受信する他のタスクシーケンスと同様に、新しいタスクシーケンスをキューに入れます。タスクシーケンスをキューに入れる必要がある場合、キュー方法で明示的に指示しない限り、そのキューイングのロジックでプリエンプト値が考慮されることはありません。
トラフィックコントロールのあるネットワークで移動タスクをプリエンプトする
プリエンプト発生時に、タスク実行者がトラフィックコントロールのあるネットワークに沿って移動している場合、プリエンプトに望ましくない副作用が伴うことがあります。タスク実行者がネットワークの端に沿って移動しているときにプリエンプトが発生した場合、タスク実行者はその端から「外れ」、移動を完了した場合に到着していたはずの次のネットワークノードに「非アクティブな」移動状態(赤線)で接続されます。ネットワークノードがトラフィックコントロールのメンバーであり、そのトラフィックコントロールのエリアがそのノードで始まる場合(つまり、プリエンプトされたとき、タスク実行者がまだそのエリアに入っていなかった場合)、タスク実行者はトラフィックコントロールエリアに「押し込まれます」。その結果、エリア内のオブジェクトの数がそのトラフィックコントロールエリアの最大値を超えることがあります。これを避けるために、特別なロジックがタスク実行者のプリエンプトメカニズムに追加されています。そのため、タスク実行者が移動タスクから直接、別の移動タスクにプリエンプトされる場合、端から外れて次のノードに接続される代わりに、プリエンプトはredirectnetworktraveler()を呼び出します。これにより、端に沿って移動していたタスク実行者の状態を実質的に維持しますが、最終的な目的地が変更されます。端の終わりに到着すると、新しい目的地につながる新しいパスに進みます。これが発生するのは、タスク実行者がプリエンプト後に実行する最初の「継続を示す」タスクが別の移動タスクであることをプリエンプトメカニズムで検出可能な場合のみです。「継続を示す」とは、実行にある程度の時間がかかるタスクを意味します。
以下のタスクが「継続を示す」ものです。
以下のタスクは「継続を示す」ものではありません。
次の「継続を示す」タスクが移動タスクではないことをプリエンプトが検出した場合、前述のようにタスク実行者は端から外れ、トラフィックコントロールエリアに押し込まれることがあります。
ディスパッチャーによるプリエンプト
プリエンプトするタスクシーケンスがディスパッチャーに与えられる場合、明示的に指示しない限り、そのディスパッチャーではタスクシーケンスのプリエンプト値が考慮されません。最初に利用可能なタスク実行者にディスパッチャーが送付するよう設定されている場合は、それが実行され、タスク実行者にプリエンプトするタスクシーケンスがすぐに送信されることはありません。ディスパッチャーにプリエンプトするタスクシーケンスをすぐに送付させる場合は、そのロジックをその「パス先」機能に指定する必要があります。
タスク実行者からディスパッチャーまでA接続をドラッグすることで、タスク実行者を再びディスパッチャーに接続できることがあります。これが完了し、タスク実行者がプリエンプトするタスクを受信すると、その現在のタスクをディスパッチャーに渡します。ディスパッチャーは次に、そのディスパッチロジックに基づいてそのタスクを再配布します。この方法でディスパッチャーに返されるタスクは、現在の状態に返されるため、次のタスク実行者は前のタスク実行者が止めたところから始められます。これが原因となり、奇妙な動作が引き起こされることもあるため、プリエンプトするタスクを割り当てる際は考慮してください。たとえば、プリエンプトされた際に、タスク実行者がアイテムを持って移動している場合、そのタスクを受信したタスク実行者は最初のタスク実行者からアイテムを受け取らずに移動を実行し、タスクをアンロードします。それどころか、アンロードが完了すると、アイテムが正しい場所に「魔法のように」現れます。このような奇妙な動作を防ぐために、タスク実行者の状態にクエリを行い、新しいタスクを割り当てる前に「プリエンプト可能な」状態であるか判断することが推奨されます。
調整されたタスクシーケンス
調整されたタスクシーケンスは、FlexSimの廃止予定の機能です。複数のタスク実行者を調整するときは処理フローを使用することをお勧めします。以下の文章はレガシーサポート用に組み込まれています。
「調整されたタスクシーケンス」は、2人以上のタスク実行者の間で複雑な調整が必要な操作に利用されます。このようなタスクシーケンスでは、タスク実行者の割り当てと割り当て解除、並列実行される複数の操作の同期といった概念を実行します。
コマンド
「調整されたタスクシーケンス」は、デフォルトのタスクシーケンスコマンドと相互に排他的な一連のコマンドを使用して構築され、送付されます。以下は、調整されたタスクシーケンスのコマンドです。
createcoordinatedtasksequence()
insertallocatetask()
insertdeallocatetask()
insertsynctask()
insertproxytask()
dispatchcoordinatedtasksequence()
createcoordinatedtasksequence
createcoordinatedtasksequenceコマンドはパラメータを1つ、つまりオブジェクトへの参照を受け取ります。このオブジェクトは、タスクシーケンスを保持し、タスクを調整するタスクコーディネーターとして指定されます。タスクコーディネーターには、タスクシーケンス内に割り当てられたオブジェクトの1つを指定することもできます。任意のディスパッチャーまたはタスク実行者オブジェクトでも構いません。タスクコーディネーターを選択しても、そのタスクコーディネーターを割り当てたことにはなりません。タスクコーディネーターは、任意の数の「調整されたタスクシーケンス」をいつでも調整できます。また、通常のタスクシーケンスとは異なり、調整されたタスクシーケンスはキューに入りません。「調整されたタスクシーケンス」が送付されると、タスクコーディネーターはすぐに「調整されたタスクシーケンス」の実行を開始します。他にいくつ調整しているかは関係ありません。
insertallocatetask
insertallocatetaskコマンドには4つのパラメータがあります。最初のパラメータはタスクシーケンスです。2番目のパラメータは、割り当てタスクを与えるタスク実行者またはディスパッチャーです。実際は、タスクコーディネーターが割り当てタスクまで進むと、別個のタスクシーケンスを作成し、その中に割り当てタスクを入れます。そして指定のタスク実行者またはディスパッチャーにそのタスクシーケンスを渡します。ディスパッチャーの場合、つまり、タスク実行者の1人を割り当てる場合、割り当てられる特定のタスク実行者を参照するキーとしてこのコマンドの戻り値を使用できます。タスクシーケンスの構築時、どのタスク実行者になるのか正確にわからないためです。3番目と4番目のパラメータは、作成される別個のタスクシーケンスの優先度の値とプリエンプト値です。5番目のパラメータは任意であり、タスクがブロックしているかどうかを指定します。デフォルト(0)では、タスクはブロックされています。1が渡された場合、タスクはブロックされません。
insertproxytask
insertproxytaskコマンドはaddTaskメソッドに似ていますが、パラメータを1つ持ち、2番目のパラメータが追加されています。2番目のパラメータは、タスクを実行させる割り当てオブジェクトを指定します。タスクシーケンスを実際に実行するのはタスクコーディネーターです。タスクコーディネーターがプロキシタスクまで進むと、代理でタスクを実行するように割り当てオブジェクトに指示します。involved1とinvolved2については、キーまたは直接参照をオブジェクトに渡すことができます。
insertsynctask
insertsyncタスクは、キーによって参照される指定タスクが終了するまで、タスクシーケンスの実行を停止します。これは、タスクシーケンスと指定プロキシタスクのキー値という2つのパラメータを受け取ります。同期タスクが指定されていない限り、デフォルトでは、異なるタスク実行者に指定されているプロキシタスクは並列実行される一方、同じタスク実行者に与えられたプロキシタスクは順番に自動で実行され、同期タスクは求められないことに注意してください。これは重要なことです。
insertdeallocatetask
insertdeallocatetaskコマンドは、そのキーによって参照される特定のタスク実行者の割り当てを解除します。最初のパラメータは、調整されたタスクシーケンスを参照します。2番目のパラメータは、割り当てを解除するリソースの割り当てキーです。3番目のパラメータは任意であり、タスクがブロックしているかどうかを指定します。デフォルト(0)では、タスクはブロックされています。1が渡された場合、タスクはブロックされません。
上のコードは、下の図に示すように、2つのタスクシーケンスを構成する「調整されたタスクシーケンス」を作成します。
調整されたタスクシーケンス
例
3人のオペレーターからなるチームが2つのフォークリフトを共有しています。1つのオペレーションには、1つのオペレーターと1つのフォークリフトが必要です。オペレーターはフォークリフトまで移動し、フォークリフトはオペレーターを載せます。次に、フォークリフトは積荷場所まで移動し、アイテムを拾い、荷下ろし場まで移動し、アイテムを降ろします。次に、フォークリフトは駐車場所まで移動し、オペレーターを降ろします。シンプルなタスクシーケンスを使用してこれを行うのは非常に困難です。極めて協調的に動作する2つの異なるリソースを扱うからです。「調整されたタスクシーケンス」を利用すれば、この例はシミュレーションがずっと簡単になります。下の図は、フォークリフトとオペレーターに対して実行する必要がある2つのタスクシーケンスを示しています。一方のリソースが作業中は、他方は何もしないで待機が必要な部分がある点に注意してください。
コード
このタスクシーケンスを構築するコードは、次のように記述されます。operatorteamとforkliftteamと呼ばれる参照が確立されていると仮定します。それぞれ、3つのオペレーターオブジェクトと2つのトランスポーターオブジェクトへのディスパッチャーを参照します。荷を積むロードステーション、荷をアンロードステーション、アイテムにも参照が確立されています。
treenode ts = createcoordinatedtasksequence(operatorteam);
int opkey = insertallocatetask(ts, operatorteam, 0, 0);
int forkliftkey = insertallocatetask(ts, forkliftteam, 0,0);
int traveltask = insertproxytask(ts, opkey, TASKTYPE_TRAVEL, forkliftkey, NULL);
insertsynctask(ts, traveltask);
insertproxytask(ts, forkliftkey, TASKTYPE_MOVEOBJECT, opkey, forkliftkey);
insertproxytask(ts, forkliftkey, TASKTYPE_TRAVEL, loadstation, NULL);
insertproxytask(ts, forkliftkey, TASKTYPE_LOAD, item, loadstation);
insertproxytask(ts, forkliftkey, TASKTYPE_TRAVEL, unloadstation, NULL);
insertproxytask(ts, forkliftkey, TASKTYPE_UNLOAD, item, unloadstation);
insertproxytask(ts, forkliftkey, TASKTYPE_TRAVEL, forkliftteam, NULL);
insertproxytask(ts, forkliftkey, TASKTYPE_MOVEOBJECT, opkey, model());
insertdeallocatetask(ts, forkliftkey);
insertdeallocatetask(ts, opkey);
dispatchcoordinatedtasksequence(ts);
覚えておくべきこと
- リソースプロキシタスクを与える前に実行する最初のことは、そのリソースを割り当てることです。
- 後で使用するため、各割り当てタスクからキーを取り返す必要があります。insertproxytaskコマンドは、プロキシタスクの実行者のためのキーを取得します。これは割り当てタスクが返すキーです。また、オブジェクトの割り当てを解除するときにもこのキーを使用します。
- 同じ割り当てリソースのプロキシタスクはすべて順番に実行されますが、調整されたタスクシーケンスにブロッキングタスクを明示的に配置しない限り、異なる割り当てリソースのプロキシタスクは並列で実行されます。
- ブロッキングタスクは、調整されたタスクシーケンスの並列実行をブロックするタスクです。タスクコーディネーターは、ブロッキングタスクに遭遇するまで、タスクシーケンスを進め、適切な割り当てリソースにプロキシタスクを与えます。ブロッキングタスクに遭遇した場合、タスクのブロッキング要件が満たされるまで待機し、満たされたらタスクシーケンスを続行します。つまり、そのブロッキングタスクの後に発生したすべてのタスクの実行は、(適用されるリソースに関係なく)ブロッキングタスクの要件が満たされるまで停止となります。ブロッキングタスクとそのブロッキング要件は次のとおりです。
- 割り当てタスク:デフォルトでは、このタスクは指定リソースが割り当てられるまでブロックします。ただし、insertallocatetaskの5番目のパラメータが1の場合、割り当てタスクはブロックしません。
- 同期タスク:このタスクは、そのキーで指定されたプロキシタスクが終了するまでブロックします。
- 割り当て解除タスク:デフォルトでは、このタスクは、指定リソースがそのすべてのプロキシタスクを終了し、割り当てが解除されるまでブロックします。ただし、insertdeallocatetaskの3番目のパラメータが1の場合、割り当て解除タスクはブロックしません。
- タスクを挿入する順序には微妙ですが重要な意味があります。これは特に、ブロッキングタスクとの関連でプロキシタスクを配置するときに当てはまります。特定のブロッキングタスクの後に配置されたプロキシタスクは、それらのプロキシタスクがブロッキングタスクの前に挿入された場合とは非常に異なる形式で実行されることがあります。
- 割り当てたすべてのオブジェクトの割り当てを解除してください。解除しない場合、タスクシーケンスでは、割り当てられたオブジェクトが適切に解放されません。
- リソースの割り当てを解除したら、それ以上はプロキシタスクを与えないでください。