2012-07-06 8 views
7

Ich hoffe, dass mir jemand bei diesem Problem helfen kann, oder zumindest die wegen meiner Fehler hinweisen ...Boost-Statechart - Lokale Übergänge

Als einfache Darstellung meines Problems betrachten einen Teil eine Anwendung, in der Sie einen Betriebszustand "Funktionsmodus" eingeben können. Je nachdem, welche Funktionstaste F1-F4 der Benutzer drückt, stehen dann vier Untermodi zur Verfügung. Standardmäßig ist der F1-Modus aktiviert. Das Zustandsdiagramm beginnt wie folgt:

Diagram 1

Der Benutzer F1-F4 zu jeder Zeit zu dem entsprechenden Modus schalten drücken. Addiert man diese Übergänge zu den inneren Zuständen führt zu folgendem:

Diagram 2

Offensichtlich ist dies (a) ein Durcheinander, und (b) eine Menge von Übergängen zu definieren. Wenn ich irgendwann einen F5Mode hinzufügen möchte, dann ... nun, du bekommst das Bild. Um dies zu vermeiden Ich möchte folgendes tun:

Diagram 3

Boost-Statechart ermöglicht es mir, von FunctionMode einem der inneren Zustände Übergänge zu definieren, aber das Ergebnis ist nicht das, was ich erwartet hatte. Das tatsächliche Ergebnis ist wie folgt:

Diagram 4

D.h. Drücken von F1-F4 zum Umschalten zwischen den Modi bewirkt, dass der äußere FunctionMode-Status verlassen und erneut eingegeben wird, während die unerwünschten Exit- und Entry-Aktionen ausgelöst werden.

Weg zurück in 2006, this thread zwischen dem Bibliotheksautor und einem Benutzer scheint das gleiche Problem zu beschreiben. Ich denke, dass der Autor die folgenden als Behelfslösung schlägt tun:

Diagram 5

jedoch, dass Behelfslösung nicht sehr attraktiv für mich scheint: Es hat eine zusätzliche staatliche Ebene hinzugefügt werden zusammengestellt, der Code ist weniger lesbar geworden, deep-history müsste verwendet werden, um zu irgendeinem der Funktionsmodus-Unterzustände zurückzukehren, und das Zwischenzustandsobjekt wird unnötigerweise zerstört und erneut aufgebaut.

Also ... wo liege ich falsch? Oder was sind die Alternativen? Ich habe einen kurzen Blick auf Boost Meta State Machine (msm) geworfen, aber von dem, was ich bisher gesehen habe, bevorzuge ich das Aussehen von Statechart.

Ich bin überrascht, dass mehr Benutzer nicht das gleiche Problem konfrontiert haben ... was mich denken lässt, dass mein Ansatz möglicherweise völlig falsch ist!

Antwort

1

Haben Sie sich die in-state reaction in der statechart tutorial erklärt? Es scheint zu tun, wonach Sie suchen.

Da Sie nach Alternativen fragen, evaluiere ich in diesem Zeitraum verschiedene C++ - Harel Statechart-Implementierungen. Ich schaute Boost Statechart und Boost MSM. Ich habe Code mit beiden geschrieben. Sie verletzen mein schwaches Gehirn :-)

Dann fand ich Machine Objects (Macho), sehr einfach und klein, und ich liebe es. Es unterstützt hierarchische Zustandsautomaten, Ein-/Ausstiegsaktionen, History, Zustandsautomaten-Snapshots, Wächter, interne Übergänge, Event-Deferring, State-Local-Storage (mit optionaler Persistenz), also ist es eine befriedigende Harel Statechart-Implementierung.

Dieser Code implementiert die FunctionMode Teil des Zustandsdiagramms mit 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; 
} 

es läuft, wird die Ausgabe ist:

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

, die zeigt, dass die Übergänge intern sind.

Meiner Meinung nach ist sauber, einfach und kompakt Code :-)

[EDIT1] Die erste Version des Codes nicht den anfänglichen Übergang durchführte FunctionMode ->F1Mode. Jetzt tut es das.

+0

Vielen Dank dafür und Entschuldigung für die Verzögerung bei der Antwort, es sieht so aus, als hätte ich keine E-Mail-Benachrichtigungen aktiviert. Unglücklicherweise haben In-State-Reaktionen das gleiche Problem - das Aufrufen von Transit <> aus dem äußeren Zustand, um den inneren Zustand zu ändern, bewirkt immer noch, dass der äußere Zustand verlassen und erneut betreten wird. Wie auch immer, die vorgeschlagene Alternative sieht für mich ziemlich gut aus, also danke dafür! – Grant

+0

@Grant: danke, und ich bin froh, dass es dir gefällt :-) –