Trabajar con código de máquina de estados
La clase primaria de la máquina de estados (es decir, la clase controladora controller o la clase de contexto) es la única interfaz que existe entre el usuario de la máquina de estados y su implementación.
La clase controladora controller aporta los métodos que se pueden usar desde "fuera" para cambiar los estados (p. ej. después de que tengan lugar eventos externos).
No obstante, la implementación de la máquina de estados llama a los métodos de la clase controller (devoluciones de llamada) para informar al usuario de la máquina de estados sobre cambios de estado (OnEntry, OnExit, ...), efectos de las transiciones y la posibilidad de invalidar e implementar métodos para condiciones (guardas).
UModel puede crear operaciones simples (sin parámetros) automáticamente para comportamientos entrar/salir/hacer, efectos de transición, etc. cuando se activa la opción correspondiente (consulte el apartado Crear estados, actividades y transiciones.) Estos métodos se pueden cambiar (añadiéndoles parámetros, configurándolos como métodos abstractos, etc.).
Puede generar instancias de una máquina de estados (es decir, de su clase controladora controller) y todas las instancias funcionan independientemente.
•La ejecución de la máquina de estados UML está diseñada para el modelo de ejecución hasta el final.
•Las máquinas de estados UML suponen que el procesamiento de cada evento finaliza antes de que empiece a procesarse el siguiente evento.
•Esto también significa que las acciones entrar/salir/hacer y los efectos de las transiciones no pueden disparar transiciones/cambios de estado nuevos directamente.
Inicialización
•Cada región de una máquina de estados debe tener un estado inicial.
•El código generado con UModel inicializa automáticamente todas las regiones de la máquina de estados (o cuando se llama al método Initialize() de la clase controladora).
•Si no necesita eventos OnEntry durante la inicialización, puede llamar a mano al método Initialize() e ignorar los eventos OnEntry durante el inicio.
Obtener el estado actual
UModel admite estados compuestos y estados ortogonales, así que no hay un solo estado actual: cada región (de cualquier nivel jerárquico) puede tener un estado actual.
En el proyecto de ejemplo AirCondition.ump puede ver cómo se pueden recorrer las regiones hasta llegar a los estados actuales:
TreeNode rootNode = m_CurrentStateTree.Nodes.Add(m_STM.getRootState().getName()); |
Ejemplo nº1: una transición simple
La operación correspondiente se genera automáticamente en UModel.
Método generado en el código:
private class CTestStateMachine : IState |
Notas:
•El usuario de la máquina de estados debería llamar al método generado "MyEvent1" cuando tenga lugar el evento correspondiente (fuera de la máquina de estados).
•El parámetro de devolución de estos métodos-evento aporta información si el evento provocó un cambio de estado (es decir, si tuvo un efecto o no en la máquina de estados). Por ejemplo, si "State1" está activo y ocurre el evento "MyEvent1()", entonces el estado actual cambia a "State2" y "MyEvent1()" devuelve true. Si "State2" está activo y ocurre el evento "MyEvent1()", nada cambia en la máquina de estados y MyEvent1() devuelve false.
Ejemplo nº2: una transición simple con un efecto
La operación correspondiente se genera automáticamente en UModel
Método generado en el código:
private class CTestStateMachine : IState |
Notas:
•La implementación de la máquina de estados llamará a "OnState1State2Effect()" cuando se dispare la transición del estado "State1" al estado "State2".
•Para reaccionar a este efecto "OnState1State2Effect()" debería sobrescribirse en una clase derivada de "CTestStateMachine".
•"CTestStateMachine:: OnState1State2Effect()" también puede configurarse como abstract y obtendrá errores de compilación hasta que se sobrescriba el método.
•Cuando "OnState1State2Effect()" no es abstracto y está activa la opción Generar mensajes de depuración, UModel genera este resultado:
// Override to handle entry/exit/do actions, transition effects,...: |
Ejemplo nº3: una transición simple con un efecto y un parámetro
La operación correspondiente se genera automáticamente en UModel.
Método generado en el código:
private class CTestStateMachine : IState |
Notas:
•Para llevar a cabo las operaciones (creadas automáticamente por UModel), puede añadir parámetros manualmente (UModel no puede conocer el tipo necesario).
•En este ejemplo el parámetro "text:String" se añadió al método Effect de TestController. Es necesario especificar un argumento adecuado cuando se llame a este método (en este caso: "1 => 2").
•Otra posibilidad es llamar a los métodos estáticos ("MyStatic.OnState1State2Effect("1 => 2")") o a los métodos de singleton ("getSingleton().OnState1State2Effect("1 => 2")").
Ejemplo nº4: acciones entrar/salir/hacer
Las operaciones correspondientes se generan automáticamente en UModel.
Método generado en el código:
private class CTestStateMachine : IState |
Notas:
•Los estados pueden tener comportamientos entrar/salir/hacer. UModel crea automáticamente las operaciones necesarias para ellos.
•Cuando tiene lugar "MyEvent2()", la implementación de la máquina de estados llama a "OnExitState3()", si "MyEvent2" tuviera un efecto, se le llamaría después y posteriormente se llamaría a "OnEntryState4" y "OnDoState4".
•Por lo general estos métodos deberían sobrescribirse. Cuando no son abstractos y está activa la opción Generar mensajes de depuración, UModel genera el resultado que se describe en el ejemplo nº2.
•Estos métodos también pueden tener los parámetros que aparecen en el ejemplo nº3.
Ejemplo nº5: guardas
Las transiciones pueden tener guardas, que determinan si la transición se dispara realmente.
La operación correspondiente se genera automáticamente en UModel.
Método generado en el código:
private class CTestStateMachine : IState |
Notas:
•Si "State5" es el estado activo y tiene lugar "MyEvent2", la implementación de la máquina de estados llamará a "CanGoState6" y, dependiendo de su resultado, la transición se disparará o no.
•Por lo general estos métodos deberían sobrescribirse. Cuando no son abstractos y está activa la opción Generar mensajes de depuración, UModel genera el resultado que se describe en el ejemplo nº2.
•Estos métodos también pueden tener los parámetros que aparecen en el ejemplo nº3.
•Varias transiciones pueden tener el mismo evento, pero guardas diferentes. No hay un orden definido para sondear los guardas. Si una transición no tiene guarda o si su guarda es "else", se trata como la última transición (es decir, esta transición solo se disparará si los guardas de las demás transiciones devuelven false. Por ejemplo, no está definido si primero se dispara CanGoState6 o CanGoState7, pero lo que está claro es que la tercera transición solo se disparará si CanGoState6 y CanGoState7 devuelven false.
Para ver más funciones y construcciones consulte los ejemplos de los archivos AirCondition.ump y Complex.ump.