2012-07-06 11 views
7

estoy esperando que alguien me puede ayudar con este problema, o al menos señalar el error de mis caminos ...Boost Statechart - transiciones locales

Como un simple ejemplo de mi problema de tener en cuenta una parte de una aplicación donde puede ingresar al estado de operación "Modo de Funciones". Cuatro submodos están disponibles dependiendo de la tecla de función F1-F4 que el usuario presione. Por defecto, se ingresa el modo F1. El diagrama de estado comienza como sigue:

Diagram 1

El usuario puede pulsar F1-F4 en cualquier momento para cambiar al modo correspondiente. La adición de estas transiciones a los estados internos conduce a la siguiente:

Diagram 2

Obviamente, esto es (a) un desastre, y (b) una gran cantidad de transiciones de definir. Si en algún momento quiero agregar un F5Mode, entonces ... bueno, ya entiendo la imagen. Para evitar esto me gustaría hacer lo siguiente:

Diagram 3

Boost Statechart me permite definir las transiciones de FunctionMode a cualquiera de los estados internos, pero el resultado no es lo que esperaba. El resultado real es la siguiente:

Diagram 4

es decir, al presionar F1-F4 para cambiar los modos, se sale del estado de FunctionMode externo y se vuelve a ingresar junto con la activación de las acciones de entrada y salida no deseadas.

Ya en 2006, this thread entre el autor de la biblioteca y un usuario parece describir el mismo problema. Creo que el autor sugiere hacer lo siguiente como una solución temporal:

Diagram 5

Sin embargo, esa solución alternativa no parece muy atractivo para mí: Se ha añadido un nivel de estado adicional para ser compilado, el código se ha vuelto menos legible, la historia profunda tendría que usarse para regresar a cualquiera de los subestados de modo de función y el objeto de estado Intermedio se destruye innecesariamente y se construye de nuevo.

Entonces ... ¿dónde me estoy equivocando? O cuales son las alternativas? He echado un vistazo breve a Boost Meta State Machine (msm), pero por lo que he visto hasta ahora, prefiero mucho el aspecto de Statechart.

Me sorprende que más usuarios no hayan enfrentado el mismo problema ... lo que me hace pensar que tal vez mi enfoque sea completamente incorrecto.

Respuesta

1

¿Has mirado el in-state reaction explicado en el statechart tutorial? Parece estar haciendo lo que estás buscando.

Como usted está pidiendo alternativas, en este período estoy evaluando varias implementaciones de C++ Harel Statechart. Miré Boost Statechart y Boost MSM. Escribí el código con ambos. Herieron mi débil cerebro :-)

Luego encontré Machine Objects (Macho), muy simple y pequeño, y me encanta. Admite máquinas de estado jerárquico, acciones de entrada/salida, historial, instantáneas de máquina de estado, guardias, transiciones internas, diferimiento de eventos, almacenamiento de estado local (con persistencia opcional), por lo que para mí es una implementación satisfactoria del diagrama de estado de Harel.

Este código implementa la parte FunctionMode de la 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; 
} 

de ejecutarlo, la salida es:

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

que muestra que las transiciones son internos.

En mi opinión, limpia, fácil y compacta código :-)

[EDIT1] La primera versión del código no realizaron la transición inicial FunctionMode ->F1Mode. Ahora lo hace.

+0

Muchas gracias por eso y me disculpo por la demora en responder, parece que no tuve notificaciones por correo electrónico activadas. Desafortunadamente, las reacciones en el estado tienen el mismo problema: llamar a Transit <> desde el estado externo para cambiar el estado interno sigue causando que se salga del estado externo y se vuelva a ingresar. Sin embargo, tu alternativa sugerida parece muy buena para mí, ¡así que gracias por eso! – Grant

+0

@Grant: gracias, y me alegra que te guste :-) –

Cuestiones relacionadas