In che modo una macchina a stati figlio può rinviare il controllo alla macchina dello stato genitore?

9

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

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

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

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

Se la macchina dello stato genitore passa da A a B, subentra la macchina a stati di B. Una volta che B è finito, come dovrebbe rinunciare al controllo sulla macchina dello stato genitore e passare allo stato C? Quale modello di progettazione usi?

Se vi state chiedendo, ho macchine a stati per bambini all'interno di macchine a stati parentali perché il mio progetto esatto è piuttosto complesso ed è naturale incapsulare il funzionamento interno di uno stato figlio.

    
posta JoJo 09.02.2012 - 19:35
fonte

4 risposte

5

Ogni macchina di stato ha una sorta di gestore di eventi e un mezzo per attivare quegli eventi. Questo gestore prende come input lo stato e il tipo di evento esistenti, sceglie il nuovo stato e, facoltativamente, esegue un codice di effetti collaterali.

Essenzialmente, mentre è nello stato B , il gestore di eventi principale inoltra tutti gli eventi che non riconosce al gestore di eventi di B e rimane nello stato B . Quando B vuole passare a C , pubblica l'evento appropriato sul gestore di eventi principale.

    
risposta data 09.02.2012 - 21:07
fonte
2

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

Scopri come sono stati creati e guarda se c'è un modo canonico per comunicare. Se uno non esiste, potresti pensare che il tuo sistema sia sbagliato.

Per quanto mi riguarda, guarderei i processi separati, collegando lo stdin e lo stdout insieme. La macchina dello stato secondario diventa autonoma, agisce su stdin e genera output su stdout. Diventa il lavoro della macchina dello stato genitore per avviare il processo figlio, collegare i tubi, quindi eseguire il dump dei dati e attendere i risultati. Tutte queste cose sono già state fatte in tutte le lingue moderne, quindi dovrebbe essere facile da fare.

    
risposta data 09.02.2012 - 20:51
fonte
1

Separa le due macchine a stati e usa il messaggio che passa tra di esse. Quindi, la macchina di stato 1 procederà da ABC, dove nello stato B controlla i risultati correnti dalla macchina di stato 2. Se l'output è cambiato, allora la macchina di stato 1 può renderlo conto e lo stato macchina 2 non ha bisogno di essere informato di come funziona la macchina di stato 1. Qualcosa come:

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;
  }
}
    
risposta data 09.02.2012 - 21:31
fonte
1

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

Se li hai isolati tramite namespace e / o A, B e C non hanno un genitore comune, allora il tuo miglior wayy è avere un driver di cambiamento di stato esterno per le macchine A, B e C. Questo può essere fatto tramite un gestore di eventi. È sufficiente disporre di un osservatore in A che possa ascoltare gli eventi generati in B e le transizioni nel proprio stato secondario in base all'evento.

    
risposta data 10.02.2012 - 12:07
fonte

Leggi altre domande sui tag