La máquina de estado es una de las arquitecturas fundamentales que los desarrolladores de LabVIEW utilizan con frecuencia para crear aplicaciones rápidamente. La arquitectura de máquina de estado se puede utilizar para implementar algoritmos complejos de toma de decisiones que son representados por diagramas de estado o diagramas de flujo. Una máquina de estado se puede implementar usando funciones innatas de LabVIEW; no se requieren toolkits o módulos adicionales para la arquitectura.
Este artículo explica qué es una máquina de estado, explica ejemplos de casos de uso, algunos ejemplos conceptuales de una máquina de estado y un ejemplo de código de una máquina de estado.
Una máquina de estado es una arquitectura de programación que permite el flujo dinámico a los estados según los valores de los estados anteriores o la información del usuario.
Esta arquitectura es ideal para aplicaciones que pueden describirse como una combinación de:
Un estado se puede definir como el estado dentro del programa mientras se realiza la tarea general del programa; ejemplos de estados pueden ser inicializar, esperar, ejecutar un cálculo, verificar el estado, etc.
La declaración lógica ayuda a determinar cuándo pasar a un nuevo estado y a qué estado pasar. Los eventos se pueden utilizar para activar el movimiento de un estado al siguiente; estos pueden ser eventos programáticos o definidos por el usuario, como presionar un botón.+
Cada estado en una máquina de estado hace algo único y llama a otros estados. La comunicación de estados depende de alguna condición o secuencia. Para traducir el diagrama de estado a una arquitectura de programación de LabVIEW, se necesita la siguiente infraestructura:
Las máquinas de estado se utilizan en aplicaciones donde existen estados identificables. Cada estado puede llevar a uno o varios estados y también puede finalizar el flujo del proceso. Una máquina de estado se basa en la información del usuario o en el cálculo en el estado para determinar qué estado pasar al siguiente. Muchas aplicaciones requieren un estado de "inicialización", seguido de un estado predeterminado en el que se pueden realizar acciones diferentes. Las acciones realizadas pueden depender de las entradas anteriores y actuales, así como de los estados. Después se puede utilizar un estado de "apagado" para realizar acciones de limpieza.
Además de su poderosa capacidad para implementar algoritmos de toma de decisiones, las máquinas de estado también son formas funcionales de planificar aplicaciones. A medida que crece la complejidad de las aplicaciones, también crece la necesidad de un diseño adecuado. Los diagramas de estado y los diagramas de flujo son útiles y algunas veces, esenciales para el proceso de diseño. Las máquinas de estado no solamente son ventajosas para planificar aplicaciones, sino que también son fáciles de crear.
Por ejemplo, las siguientes aplicaciones pueden beneficiarse del patrón de máquina de estado:
Existe otro patrón de diseño que se puede utilizar para implementar una interfaz de usuario, el Controlador de Mensajes en Cola. Un controlador de mensajes en cola es una versión más sofisticada de la máquina de estado y ofrece flexibilidad adicional, pero también agrega complejidad.
Crear una máquina de estado efectiva requiere que el diseñador (1) haga una lista de los posibles estados. Con esta lista, el diseñador puede (2) planificar cómo se relaciona cada estado con otro. Luego, el diagrama de estado puede (3) traducirse a la arquitectura de programación gráfica de LabVIEW.
En este ejemplo, queremos generar una aplicación que dispare un cañón continuamente sin permitir que se caliente de manera peligrosa.
(1) Lista de posibles estados
Para comenzar, hacemos una lista de todos los estados posibles para nuestra tarea. Para lograr la tarea de disparar continuamente el cañón se necesita:
(2) Asignar las relaciones de los estados en un diagrama de estado
Después, consideraremos cómo se relacionan estos estados entre sí y crearemos un diagrama de estado. Al crear el diagrama de estado, considere qué provocaría que el programa se moviera de un estado al siguiente, ¿es una transición automática? ¿Existe un disparo externo por parte del usuario? ¿La transición se basa en el resultado de un cálculo?
Por ejemplo, consideremos las relaciones entre el estado de inicialización y otros estados.
Note que al pensar en cómo los estados se relacionan entre sí, hemos comenzado a definir la lógica para moverse entre estos estados. La lógica de programación que usaremos en el código depende de (1) el número de posibles estados de transición y (2) el número de entradas que se consideran en la lógica. Las opciones para la transición de código se analizan a continuación en la sección Ejemplos de código de transición.
Continúe trabajando en cómo los estados se relacionan entre sí hasta que tenga un diagrama de estado completo. El diagrama de estado incluirá todos los estados y la relación entre ellos (Figura 1). Observe que en este diagrama, los estados (nodos ovalados) describen las acciones que se realizan cuando el proceso de control está en ese estado, mientras que las transiciones (flechas) simplemente describen cuándo y cómo el proceso puede pasar de un estado a otro.
Figura 1: Diagrama de estado para disparar un cañón
La relación entre cada estado le ayudará a programar la lógica necesaria para pasar de un estado al siguiente.
(3) Construir una máquina de estado en LabVIEW
Después de definir los estados y su relación en un diagrama de estado, puede traducir esto a la arquitectura de codificación en LabVIEW (Figura 2). El flujo entre los estados del diagrama de estado (Figura 1) es implementado por el ciclo. Los estados individuales son reemplazados por casos en la estructura de casos. Cada estado del diagrama anterior corresponde a un subdiagrama de la estructura de caso. Cada estado:
Un registro de desplazamiento en el ciclo While realiza un seguimiento del estado actual, el cual se alimenta a la entrada de la estructura de caso.
Figura 2: Máquina de estado
Consulte el proyecto de ejemplo Single Shot Measurement, disponible en la ventana de diálogo Create Project, para tener un ejemplo sobre adaptar esta plantilla a una aplicación de medidas.
1. El código de apagado se ejecuta únicamente cuando el usuario desea detener la aplicación.
2. El código de apagado siempre se ejecuta hasta el finalizar.
Consulte el tutorial Modify the Simple State Machine LabVIEW Template para saber cómo comenzar.
Antes de personalizar la plantilla, hágase las siguientes preguntas:
Existen diferentes métodos para determinar a qué estado hacer la transición, que se analizan a continuación.
Tenga en cuenta que, si bien las imágenes de ejemplo muestran el estado "Inicialización", estas posibilidades de transición podrían aplicarse a cualquier estado.
Uno a uno: Si siempre hace la transición del estado A al estado B, no es necesario programar ninguna lógica, simplemente envíe el nombre del siguiente caso (caso B) al registro de desplazamiento.
Figura 3a: Solamente un posible estado de transición
Uno a dos: Si puede hacer la transición del estado A al estado B o al estado C, puede usar una función Select para evaluar el estado de un indicador. Debe evaluar algo que determine a qué estado desea pasar. Por ejemplo, en la Figura 3b vemos que la información del usuario del botón Stop determina si pasamos del estado de encendido o procedemos al apagado.
Figura 3b: Solamente dos posibles estados de transición
Uno a varios usando arreglos: Si tiene varios estados a los que podría hacer la transición, podría usar un área booleana asociada con una constante enum para programar la transición. Por ejemplo, en la Figura 3c, se lleva a cabo algún código donde el resultado del código determina la transición, cuya salida es un arreglo de valores booleanos. El arreglo booleano se correlaciona con una constante Enum que contiene una lista de posibles estados a los que se podría hacer la transición. Usando la función Index Array, se genera el índice del primer booleano “Verdadero” en el arreglo booleano. Luego, usando la función Array Subset, puede extraer lo apropiado de la constante Enum con la que se correlaciona.
Figura 3c
Consejo: Recuerde que los arreglos tienen un índice 0 y las enums tienen un índice 1. Para correlacionar el arreglo booleano con la constante Enum, use la función Increment para corregir ese desplazamiento.
Uno a varios usando ciclo While: Otra opción, si tiene varios estados de transición posibles, es usar un ciclo While dentro de un caso. El código dentro del ciclo While continuará hasta que un estado booleano se establezca en verdadero, activando el botón Stop. Esto permitirá que el código se ejecute de manera efectiva hasta que se produzca un evento de disparo.
La figura 3d muestra un estado de "Inicialización" utilizando un ciclo interno y una estructura de caso para pasar al siguiente estado. La estructura de caso interna contiene un diagrama para cada transición que sale del estado actual. Cada uno de los casos en la estructura de caso interna tiene dos salidas: un valor booleano, que especifica si se debe hacer la transición o no, y una constante enumerada, que especifica el estado al que va la transición. Al usar el índice del ciclo como una entrada a la estructura de caso, este código se ejecuta efectivamente a través de cada caso de transición uno por uno, hasta que encuentra un diagrama con una salida booleana “Verdadera”. Una vez que se encuentra la salida booleana "Verdadera", el caso muestra el nuevo estado al que va la transición. Aunque este método puede parecer un poco más complicado que los métodos anteriores, ofrece la capacidad de agregar nombres a las transiciones al "convertir" la salida del índice de ciclo en un tipo enumerado. Este beneficio le permite agregar "documentación automática" a su código de transición.
Figura 3d
Redundancia de código
Problema: La parte más difícil de crear una máquina de estado es diferenciar entre los posibles estados en el diagrama de estado. Por ejemplo, en el diagrama de estado de la máquina de Coca Cola (Fig. 4), podríamos haber tenido estados de 0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50 centavos en lugar de tener un estado "wait for response" que va de un estado a otro dependiendo del tipo de moneda que se inserte. Eso crearía 11 estados diferentes con exactamente el mismo diagrama de caso. El código redundante puede crear un gran problema en una aplicación más grande.
Solución : Si diferentes estados tienen el mismo diagrama de caso, intente combinarlos en un solo estado. Por ejemplo, el estado "wait for response" se crea para evitar la redundancia del código.
Uso de enum
Problema: Las enums se utilizan ampliamente como selectores de casos en las máquinas de estado. Si el usuario intenta agregar o eliminar un estado de esta enum, se romperán los cables conectados restantes de las copias de esta enum . Este es uno de los obstáculos más comunes al implementar máquinas de estado con enums.
Solución: Dos posibles soluciones a este problema son:
1. Si todas las enums se copian de la enum modificada, las interrupciones desaparecerán.
2. Crear un nuevo control con la enum y seleccionar "typedef" en el submenú. Al seleccionar typedef, todas las copias de enum se actualizarán automáticamente si el usuario agrega o elimina un estado.
Si está interesado en aprender más sobre máquinas de estado y otras arquitecturas avanzadas en LabVIEW, considere tomar el curso LabVIEW Core 2.