Domanda

Sto cercando un po 'generale

  1. ottimizzazione
  2. Correttezza
  3. estensibilità

consigli sulla mia attuale implementazione C ++ Hierarchical State Machine.

Esempio

variable isMicOn = false
variable areSpeakersOn = false
variable stream = false
state recording
{
        //override block for state recording
        isMicOn = true //here, only isMicOn is true              
        //end override block for state recording
}
state playback
{
        //override block for state playback
        areSpeakersOn = true //here, only areSpeakersOn = true
        //end override block for state playback
        state alsoStreamToRemoteIp
        {
                //override block for state alsoStreamToRemoteIp
                stream = true //here, both areSpeakersOn = true and stream = true
                //end override block for state alsoStreamToRemoteIp
        }
}

goToState(recording)
goToState(playback)
goToState(playback.alsoStreamToRemoteIp)

Attuazione

Attualmente, l'HSM è implementato come una struttura ad albero in cui ogni stato può avere un numero variabile di stati come figli.

Ogni stato contiene un numero variabile di " override " blocchi (in una std :: map) che sovrascrivono i valori di base. Allo stato radice, la macchina a stati ha un set di variabili (funzioni, proprietà ...) inizializzate su alcuni valori predefiniti. Ogni volta che entriamo in uno stato figlio, un elenco di "sostituisce". definire variabili e valori che dovrebbero sostituire le variabili e i valori con lo stesso nome nello stato padre. Originale aggiornato per maggiore chiarezza.

Variabili di riferimento

In fase di esecuzione, gli stati correnti sono memorizzati in uno stack.

Ogni volta che si fa riferimento a una variabile, viene eseguita una camminata verso il basso dello stack cercando l'override più elevato o, in caso di non override, il valore predefinito.

Stati di commutazione

Ogni volta che si passa a un singolo frame di stato, lo stato viene inserito in uno stack.

Ogni volta che si passa a uno stato, seguo una descrizione dell'albero che mi porta dallo stato corrente allo stato radice. Quindi eseguo una discendenza dell'albero dallo stato target allo stato radice fino a quando vedo che la traccia corrente corrisponde alla traccia precedente. Dichiaro un incrocio nel punto in cui si incontrano quelle 2 tracce. Quindi, per passare allo stato target, scendo dalla sorgente, saltando dalla pila fino a quando non raggiungo il punto di intersezione. Quindi ascendo al nodo di destinazione e spingo i frame di stato nello stack.

Quindi, per l'esempio di codice sopra

Traccia di esecuzione per switch di stato

  • Stato sorgente = registrazione
  • Target State = alsoStreamToRemoteIp

  • discendenza dalla fonte = registrazione- > root (trace = [root])

  • discesa da target = alsoStreamToRemoteIp- > playback- > root (trace = [playback, root])

  • Interseca alla radice.

Per passare dalla registrazione anche a StreamToRemoteIp,

  1. Pop " registrazione " dallo stack (e chiama la sua funzione di uscita ... non definita qui).
  2. Premi " riproduzione " sullo stack (e chiama la funzione di invio).
  3. Push " alsoStreamToRemoteIp " nello stack (e chiama la funzione di invio).
È stato utile?

Soluzione

Due cose:

1: nella maggior parte dei casi rappresenta semplicemente lo stato del programma come modello e interagisce direttamente con esso o tramite il modello MVC.

2: se hai davvero bisogno di un FSM, ovvero vuoi fare casualmente un mucchio di azioni sul tuo modello, solo alcune delle quali sono consentite in determinati momenti. Poi ....

Conserva ancora lo stato del tuo programma in un Modello (o più Modelli a seconda della scomposizione e della complessità) e rappresentano stati e transizioni come.

class State:
   def __init__(self):
      self.neighbors = {}

Dove i vicini contengono un dizionario di {Action: State} , in modo che tu possa fare qualcosa del genere

someAction.execute() # Actions manipulate the model (use classes or lambdas)
currentState = currentState.neighbors[someAction]

O ancora più interessante, fai un ciclo infinito selezionando casualmente un'azione dai vicini, eseguendola e spostando lo stato indefinitamente. È un ottimo modo per testare il tuo programma.

Altri suggerimenti

Non sono sicuro di seguire tutti i dettagli qui. Tuttavia, sembra che tu stia descrivendo un'implementazione di FSM (macchina a stati finiti) in cui hai più macchine a stati. A volte, quando si verifica un evento particolare (E1) in uno stato particolare (S1) di FSM F1, è necessario inserire un nuovo FSM (chiamarlo F2) per semplificare l'elaborazione in generale).

In tal caso, quando si verifica E1 in S1, è necessario richiamare una routine di azione che riprende la lettura dell'evento e implementa F2 FSM. Quando viene richiamato, avvia l'elaborazione nello stato iniziale di F2 e gestisce i relativi eventi secondari. Quando raggiunge il suo stato finale, l'interprete per F2 termina. Potrebbe restituire alcune informazioni alla routine di azione di F1 che è stata sospesa durante l'esecuzione di F2, e lo stato successivo in F1 potrebbe esserne influenzato.

Il resto della tua descrizione - cose come 'override blocks' - non avrà molto senso per le persone senza accesso alla tua implementazione.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top