2012-07-06 1 views
7

Spero che qualcuno mi può aiutare con questo problema, o almeno sottolineare l'errore dei miei modi ...Boost Statechart - transizioni locali

Come semplice illustrazione del mio problema prendere in considerazione una parte della un'applicazione in cui è possibile immettere uno stato di funzionamento in "Modalità funzioni". Sono disponibili quattro sottomodalità a seconda del tasto funzione F1-F4 che l'utente preme. Per impostazione predefinita, viene inserita la modalità F1. Il diagramma di stato inizia come segue:

Diagram 1

L'utente può premere F1-F4 in qualsiasi momento per passare alla modalità corrispondente. Aggiungendo queste transizioni agli stati interni porta alla seguente:

Diagram 2

Ovviamente questo è (a) un disordine, e (b) un sacco di transizioni da definire. Se ad un certo punto voglio aggiungere un F5Mode allora ... beh, ottieni l'immagine. Per evitare questo vorrei fare quanto segue:

Diagram 3

Boost Statechart mi permette di definire le transizioni da FunctionMode qualsiasi stati interiori, ma il risultato non è quello che mi aspettavo. Il risultato effettivo è il seguente:

Diagram 4

Vale a dire premendo F1-F4 per passare da una modalità all'altra si provoca l'uscita dallo stato FunctionMode esterno e il rientro con l'attivazione delle azioni di uscita e di entrata indesiderate.

Nel lontano 2006, this thread tra l'autore della libreria e un utente sembra descrivere lo stesso problema. Penso che l'autore suggerisce di fare quanto segue come un work-around:

Diagram 5

tuttavia, che il lavoro intorno non sembra molto attraente per me: Si è aggiunto un livello di condizione in più per essere compilato, il codice è diventato meno leggibile, la cronologia profonda dovrebbe essere utilizzata per tornare a qualsiasi stato secondario della modalità di funzione e l'oggetto stato intermedio viene inutilmente distrutto e ricostruito.

Quindi ... dove sto andando male? O quali sono le alternative? Ho dato una breve occhiata a Boost Meta State Machine (msm) ma da quello che ho visto finora preferisco di gran lunga l'aspetto di Statechart.

Sono sorpreso che più utenti non abbiano affrontato lo stesso problema ... il che mi fa pensare che forse il mio approccio è completamente sbagliato!

risposta

1

Hai visto lo in-state reaction spiegato nello statechart tutorial? Sembra che stia facendo quello che stai cercando.

Poiché si stanno chiedendo alternative, in questo periodo sto valutando varie implementazioni di diagrammi di stato di Harley in C++. Ho dato un'occhiata a Boost statechart e Boost MSM. Ho scritto il codice con entrambi. Hanno ferito il mio cervello debole :-)

Poi ho trovato Machine Objects (Macho), molto semplice e piccolo, e lo adoro. Supporta macchine di stato gerarchiche, azioni di ingresso/uscita, cronologia, istantanee macchina di stato, guardie, transizioni interne, differimento di eventi, archiviazione locale dello stato (con persistenza opzionale), quindi per me è un'implementazione soddisfacente per lo schedulatore di Harel.

Questo codice implementa la parte FunctionMode del statechart con Macho:

#include "Macho.hpp" 

#include <exception> 
#include <iostream> 
using namespace std; 

namespace FunctionMode { 

struct FunctionMode; 
struct F1Mode; 
struct F2Mode; 

// The Top state, containing all the others. 
TOPSTATE(Top) { 
    STATE(Top) 
    // All the events of the state machine are just virtual functions. 

    // Here we throw to mean that no inner state has implemented the event 
    // handler and we consider that an error. This is optional, we could 
    // just have an empty body or log the error. 
    virtual void evF1() { throw std::exception(); } 
    virtual void evF2() { throw std::exception(); } 
    // evF3 and so on... 
private: 
    void init() { setState<FunctionMode>(); } // initial transition 
}; 

SUBSTATE(FunctionMode, Top) { 
    STATE(FunctionMode) 
    virtual void evF1() { setState<F1Mode>(); } 
    virtual void evF2() { setState<F2Mode>(); } 
    // evF3, ... 
private: 
    void entry() { cout << "FunctionMode::entry" << endl; } 
    void exit() { cout << "FunctionMode::exit" << endl; } 
    void init() { setState<F1Mode>(); } // initial transition 
}; 

SUBSTATE(F1Mode, FunctionMode) { 
    STATE(F1Mode) 
    virtual void evF1() {} // make the event an internal transition (by swallowing it) 
private: 
    void entry() { cout << "F1Mode::entry" << endl; } 
    void exit() { cout << "F1Mode::exit" << endl; } 
}; 

SUBSTATE(F2Mode, FunctionMode) { 
    STATE(F2Mode) 
    virtual void evF2() {} // make the event an internal transition (by swallowing it) 
private: 
    void entry() { cout << "F2Mode::entry" << endl; } 
    void exit() { cout << "F2Mode::exit" << endl; } 
}; 

} // namespace FunctionMode 

int main() { 

    Macho::Machine<FunctionMode::Top> sm; 
    // Now the machine is already in F1Mode. 

    // Macho has 2 methods for synchronous event dispatching: 
    // First method: 
    sm->evF1(); // <= this one will be swallowed by F1Mode::evF1() 
    // Second method: 
    sm.dispatch(Event(&FunctionMode::Top::evF2)); 

    return 0; 
} 

L'esecuzione, l'uscita è:

FunctionMode::entry 
F1Mode::entry 
F1Mode::exit 
F2Mode::entry 
F2Mode::exit 
FunctionMode::exit 

che dimostra che le transizioni sono interni.

A mio parere, pulito, semplice e codice compatto :-)

[EDIT1] La prima versione del codice non ha eseguire la transizione iniziale FunctionMode ->F1Mode. Adesso lo fa.

+0

Molte grazie per questo e scuse per il ritardo nel rispondere, sembra che non ho avuto le notifiche email attivate. Sfortunatamente, le reazioni nello stato hanno lo stesso problema: chiamare Transit <> dallo stato esterno per cambiare lo stato interno causa comunque l'uscita e il reinserimento dello stato esterno. Tuttavia, la tua alternativa suggerita mi sembra abbastanza carina, quindi grazie per questo! – Grant

+0

@Grant: grazie, e sono contento che ti piaccia :-) –