Come può una macchina a stati figlio restituire il controllo alla macchina a stati padre?


9

La mia macchina a stati di livello superiore ha alcuni stati e bordi. Chiamerò questo la macchina dello stato genitore.

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

Qualsiasi stato all'interno della macchina a stati padre può essere anche una macchina a stati. Chiamerò queste macchine statali per bambini.

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

Se la macchina a stati padre passa da A a B, subentra la macchina a stati B. Una volta che B ha terminato l'esecuzione, come dovrebbe abbandonare il controllo alla macchina a stati padre e passare allo stato C? Quale modello di design usi?

Se ti stai chiedendo, ho macchine a stati figlio all'interno di macchine a stato genitore perché il mio progetto esatto è piuttosto complesso ed è naturale incapsulare il funzionamento interno di uno stato figlio.


Immagino che B0, B1 e B2 dovrebbero sapere che sono componenti di qualcosa che il mondo esterno considera una singola unità. Quindi forse dovresti avere una MachineContainerclasse per Bquella che contiene B0, B1 e B2 e quando B2 finisce, passa il controllo al suo contenitore che poi passa a C ... Non ho mai provato niente del genere però. È un problema interessante!
FrustratedWithFormsDesigner,

2
La tua domanda ha la risposta ovvia o la tua domanda non è molto chiara. Dal punto di vista del genitore, è necessario implementarlo esattamente come se si implementasse una macchina a stati che non ha macchine a stato figlio. Accade così che gli stati vengano implementati usando macchine a stati figlio ma ciò non influisce affatto sul genitore. Inoltre, non dovrebbe influire sulle macchine a stati secondarie se non all'uscita generano solo gli eventi di livello padre.
Dunk

Risposte:


5

Ogni macchina a stati ha una sorta di gestore di eventi e un mezzo per innescare quegli eventi. Tale gestore accetta come input lo stato e il tipo di evento esistenti, sceglie il nuovo stato e facoltativamente esegue un codice effetto collaterale.

Essenzialmente, mentre è nello stato B, il gestore dell'evento principale inoltra tutti gli eventi che non riconosce al Bgestore dell'evento e rimane nello stato B. Quando Bdesidera passare a C, invia l'evento appropriato al gestore dell'evento principale.


2

Hai letto questa sezione di Taoup ? Esistono diversi modi per farlo, ma molti dipendono da come hai diviso le tue macchine a stati. Sono processi separati? Le discussioni? Oggetti?

Scopri come li hai costruiti e guarda per vedere se c'è un modo canonico per comunicare. Se uno non esiste, è possibile che si stia progettando il sistema in modo errato.

Per me, guarderei i processi separati, collegando insieme stdin e stdout. La macchina a stati figlio diventa quindi autonoma, agendo su stdin e producendo su stdout. Diventa il lavoro della macchina a stati genitore per avviare il processo figlio, collegare i tubi, quindi scaricare i dati e attendere i risultati. Tutte queste cose sono già state fatte in tutte le lingue moderne, quindi dovrebbe essere facile da fare.


1

Separare le due macchine a stati e utilizzare il messaggio che passa tra di loro. Pertanto, la macchina a stati 1 procederebbe da ABC, dove allo stato B controlla i risultati correnti dalla macchina a stati 2. Se l'uscita è cambiata, allora la macchina a stati 1 può tenerne conto e la macchina a stati 2 non deve avere alcuna consapevolezza di come funziona effettivamente la macchina a stati 1. Qualcosa di simile a:

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

La soluzione dipende da 1) se i sotto-stati di A sono visibili ai sotto-stati di B. 2) AB e C derivano da un genitore comune. Se hanno un genitore comune e la visibilità è universale, non dovresti avere troppi problemi a passare dallo stato secondario di B allo stato secondario di A.

Se li hai isolati tramite namespace e / o A, B e C non hanno un genitore comune, allora il tuo miglior modo è quello di avere un driver di modifica dello stato esterno per le macchine A, B e C. Questo può essere fatto tramite un gestore di eventi. Avere semplicemente un osservatore in A in grado di ascoltare gli eventi generati in B e passare al proprio stato secondario in base all'evento.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.