子ステートマシンは、どのようにして制御を親ステートマシンに戻すことができますか?


9

私のトップレベルのステートマシンには、いくつかのステートとエッジがあります。これを親ステートマシンと呼びます。

A ----> B ----> C

親ステートマシン内のどのステートもステートマシンにすることができます。これらの子供をステートマシンと呼びます。

           ___________
         /            \
A ----> |  B0->B1->B2  | ----> C
         \____________/

親ステートマシンがAからBに移行すると、Bのステートマシンが引き継ぎます。Bの実行が完了したら、制御を親の状態マシンに放棄し、状態Cに移行するにはどうすればよいですか?どのデザインパターンを使用していますか?

疑問に思うかもしれませんが、私は親の状態マシン内に子の状態マシンを持っています。私の正確なプロジェクトは非常に複雑であり、子の状態の内部動作をカプセル化するのは自然だからです。


B0、B1、およびB2は、外界が単一のユニットと見なすもののコンポーネントであることを知っているはずです。したがって、B0、B1、およびB2を含むMachineContainerクラスが必要になる場合があります。B2がB終了すると、制御がコンテナーに戻され、コンテナーがCに移行します。実際には、このようなことは試していません。興味深い問題です。
FrustratedWithFormsDesigner

2
あなたの質問には明白な答えがあるか、あなたの質問はあまり明確ではありません。親の観点からは、子の状態マシンを持たない状態マシンを実装するのとまったく同じように実装する必要があります。たまたま、子のステートマシンを使用して状態が実装されますが、親にはまったく影響しません。また、終了時に親レベルのイベントのみを生成する以外は、子のステートマシンには影響しません。
ダンク

回答:


5

すべてのステートマシンには、ある種のイベントハンドラーと、それらのイベントをトリガーする手段があります。そのハンドラーは、既存の状態とイベントのタイプを入力として受け取り、新しい状態を選択し、オプションでいくつかの副作用コードを実行します。

基本的に、Bメインのイベントハンドラーは、状態にある間は、認識できないイベントをイベントハンドラーに転送しB、状態を維持しますBBに移行する場合はC、適切なイベントをメインイベントハンドラにポストします。


2

あなたは読みましたTaoupのこのセクションを?これを達成するにはいくつかの異なる方法がありますが、それらの多くは状態マシンをどのように分割したかに依存します。それらは別々のプロセスですか?スレッド?オブジェクト?

それらがどのように構築されているかを理解し、それらが通信するための標準的な方法があるかどうかを確認します。存在しない場合は、システムの設計が間違っている可能性があります。

私にとっては、stdinとstdoutを一緒にフックして、別々のプロセスを調べます。子ステートマシンはスタンドアロンになり、stdinで動作し、stdoutで出力します。子プロセスを開始し、パイプを接続し、データをダンプして結果を待つことが、親状態マシンのジョブになります。これらのことはすべて、すべての現代言語で既に行われているため、簡単に実行できるはずです。


1

2つの状態マシンを分離し、それらの間のメッセージ受け渡しを使用します。したがって、ステートマシン1はABCから進み、ステートBでステートマシン2からの現在の結果をチェックします。出力が変更された場合、ステートマシン1はそれを考慮に入れることができ、ステートマシン2は意識する必要がありません。ステートマシン1が実際にどのように動作するかを示します。何かのようなもの:

typedef struct StateMachine {
  void(*Update)(); // function to update the state machine
  int Data;        // generic temp holder to survive state contexts
  int State;       // current state of our state machine
  int *Message;    // pointer to a shared integer for message passing
};

int main(void) {
  int Message = 0;
  /* NewStateMachine would malloc the struct, pass in the int reference
   * and function pointer as well as add it to a circularly linked list */
  NewStateMachine(&Message, MainLoop);
  NewStateMachine(&Message, MinorLoop);
  StateMachine *Current = StateMachine_CLL.First;

  for(;;) {
    Current->Update(Current); /* Update the current state machine */
    Current = Current->Next;  /* And the advance to the next one */
  }
}

void MainLoop(StateMachine *this) {
  switch(this.State) {
  case 0:
    CloseCoolantTank(1); /* safe to call if valve already closed */
    CloseCoolantTank(2); /* safe to call if valve already closed */
    this.State = 1;
    break;
  case 1:
    /* we have a message, do something */
    if(*this.Message) this.State = 2;          
    /* otherwise stall at this state until we get a message */
    else this.State = 1;          
    break;
  case 2:
    if(*this.Message == 1) this.State = 3;      /* warm */
    else if(*this.Message == 2) this.State = 4; /* hot! */
    else this.State = 0;                        /* cooled down, shut off valves */
    this.Message = 0;                           /* clear the message */
    break;
  case 3:
    OpenCoolantTank(1); /* opens the valve, safe to call if already open */
    this.State = 2;     /* recheck for new message */
    break;
  case 4:
    OpenCoolantTank(2); /* opens the valve, safe to call if already open */
    this.State = 3;     /* also open coolant tank 1 for extra cooling */
    break;
  }
}

/* Monitor temperature and send messages on overheat */
void MinorLoop(StateMachine *this) {
  switch(this.State) {
  case 0:
    this.Data = ReadADCValue();
    this.State = 1;
    break;
  case 1:
    if(this.Data > 150) *this.Message = 2;
    else if(this.Data > 100) *this.Message = 1;
    this.State = 0;
    break;
  }
}

1

解決策は、1)AのサブステートがBのサブステートに可視であるかどうかに依存します。2)ABとCは共通の親から派生していますか。それらに共通の親があり、可視性が普遍的である場合、Bのサブ状態からAのサブ状態への移行にあまり問題はないはずです。

名前空間を介してそれらを分離した場合や、A、B、Cに共通の親がない場合は、A、B、Cマシンの外部状態変更ドライバーを使用するのが最善の方法です。これは、イベントハンドラーを介して実行できます。Bで発生したイベントをリッスンし、イベントに基づいて独自のサブ状態に遷移できるオブザーバーをAに置くだけです。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.