¿Cómo puede una máquina de estado secundaria ceder el control a la máquina de estado principal?

9

Mi máquina de estado de nivel superior tiene algunos estados y bordes. Llamaré a esto la máquina de estado principal.

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

Cualquier estado dentro de la máquina de estado principal también puede ser una máquina de estado. Llamaré a estos niños máquinas de estado.

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

Si la máquina de estado principal pasa de A a B, la máquina de estado de B se hace cargo. Una vez que B termina de ejecutarse, ¿cómo debería ceder el control a la máquina de estado principal y pasar al estado C? ¿Qué patrón de diseño usas?

Si se está preguntando, tengo máquinas de estado de niños dentro de máquinas de estado de padres porque mi proyecto exacto es bastante complejo y es natural encapsular el funcionamiento interno de un estado de niño.

JoJo
fuente
Supongo que B0, B1 y B2 deberían saber que son componentes de algo que el mundo exterior considera una sola unidad. Entonces, tal vez tendrías que tener una MachineContainerclase Bque contenga B0, B1 y B2 y cuando B2 termine, devuelva el control a su contenedor que luego pasa a C ... Sin embargo, nunca he intentado algo como esto. ¡Es un problema interesante!
FrustratedWithFormsDesigner
2
Su pregunta tiene la respuesta obvia o su pregunta no es muy clara. Desde el punto de vista del padre, debe implementarlo exactamente como implementaría una máquina de estado que no tiene máquinas de estado hijo. Sucede que los estados se implementan mediante el uso de máquinas de estado secundarias, pero eso no afecta al padre en absoluto. Tampoco debería afectar a las máquinas de estado secundarias que, al salir, solo generan los eventos de nivel primario.
Dunk

Respuestas:

5

Cada máquina de estado tiene algún tipo de controlador de eventos y un medio para activar esos eventos. Ese controlador toma como entrada el estado existente y el tipo de evento, elige el nuevo estado y, opcionalmente, ejecuta un código de efectos secundarios.

Esencialmente, mientras está en estado B, su controlador de eventos principal reenvía los eventos que no reconoce al Bcontrolador de eventos y permanece en estado B. Cuando Bdesea realizar la transición C, publica el evento apropiado en el controlador de eventos principal.

Karl Bielefeldt
fuente
2

¿Has leído esta sección de Taoup ? Hay varias formas diferentes de lograr esto, pero muchas de ellas dependen de cómo haya dividido sus máquinas de estado. ¿Son procesos separados? ¿Hilos? ¿Objetos?

Averigua cómo los construiste y observa si hay una forma canónica para que se comuniquen. Si no existe, es posible que esté diseñando mal su sistema.

Para mí, miraría procesos separados, conectando stdin y stdout juntos. La máquina de estado secundaria se vuelve independiente, actuando en stdin y generando en stdout. Se convierte en el trabajo de la máquina de estado principal para iniciar el proceso secundario, conectar las tuberías y luego volcar los datos y esperar los resultados. Todas esas cosas ya se han hecho en todos los idiomas modernos, por lo que debería ser fácil de hacer.

Spencer Rathbun
fuente
1

Separe las dos máquinas de estado y use el mensaje que pasa entre ellas. Por lo tanto, la máquina de estado 1 procedería de ABC, donde en el estado B verifica los resultados actuales de la máquina de estado 2. Si la salida ha cambiado, entonces la máquina de estado 1 puede explicarlo y la máquina de estado 2 no necesita tener ningún conocimiento de cómo funciona realmente la máquina de estado 1. Algo como:

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;
  }
}
Renee Primos
fuente
1

La solución depende de 1) si los subestados de A son visibles para los subestados de B. 2) AB y C se derivan de un padre común. Si tienen un padre común y la visibilidad es universal, no debería tener demasiados problemas para pasar del subestado de B al subestado de A.

Si los ha aislado a través de espacios de nombres y / o A, B y C no tienen un padre común, entonces su mejor opción es tener un controlador externo de cambio de estado para las máquinas A, B y C. Esto se puede hacer a través de un controlador de eventos. Simplemente tenga un observador en A que pueda escuchar eventos generados en B y transiciones a su propio subestado según el evento.

DPD
fuente