子状态机如何将控制权交还给父状态机?


9

我的顶级状态机具有一些状态和边缘。我将其称为父状态机。

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

父状态机中的任何状态也可以是状态机。我将这些孩子称为状态机。

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

如果父状态机从A转换为B,则B的状态机将接管。一旦B完成运行,它应该如何将控制权交给父状态机并转换为状态C?您使用哪种设计模式?

如果您想知道,我在父状态机中有子状态机,因为我的确切项目非常复杂,并且封装子状态的内部工作是很自然的。


我猜想B0,B1和B2应该知道它们是外界认为是一个单元的组件。因此,也许你得有一MachineContainerB包含B0,B1和B2和B2时结束,它通过控制权交还给它的容器,然后过渡到C ......我从来没有真正尝试这样的东西,虽然。这是一个有趣的问题!
FrustratedWithFormsDesigner 2012年

2
您的问题有明显的答案,或者您的问题不是很清楚。从父级的角度来看,应该像实现没有子状态机的状态机一样完全实现它。碰巧的是,状态是通过使用子状态机来实现的,但根本不影响父状态机。它也不应影响子状态机,除非它们在退出时仅生成父级事件。
Dunk 2012年

Answers:


5

每个状态机都有某种事件处理程序和触发这些事件的方法。该处理程序将事件的现有状态和类型作为输入,选择新状态,并可选地运行一些副作用代码。

本质上,处于状态时B,您的主事件处理程序会将无法识别的所有事件转发到B事件处理程序,并保持其状态B。当B要转换为时C,它将适当的事件发布到主事件处理程序。


2

您阅读过Taoup的这一部分吗?有几种不同的方式可以做到这一点,但是许多方式取决于您如何拆分状态机。它们是分开的过程吗?线程?对象?

弄清楚它们是如何构建的,并查看它们之间是否存在规范的通信方式。如果不存在,则可能是系统设计错误。

对我来说,我将研究单独的进程,将stdin和stdout挂钩。然后,子状态机变为独立状态,作用于stdin并在stdout上输出。启动子进程,连接管道,然后转储数据并等待结果,成为了父状态机的工作。所有这些事情已经以所有现代语言完成,因此应该很容易做到。


1

分离两个状态机,并使用它们之间传递的消息。因此,状态机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机器配备一个外部状态更改驱动程序。这可以通过事件处理程序来完成。只需在A中有一个观察者,它就可以侦听B中引发的事件并根据事件转换到其自己的子状态。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.