En una arquitectura controlada por eventos, cada componente solo actúa cuando se envía un evento a través del sistema.
Imagine un automóvil hipotético con un pedal de freno y una luz de freno.
- Las vueltas de luz de freno en cuando recibe una brake_on evento y apagado cuando recibe una brake_off evento.
- El pedal de freno envía un evento brake_on cuando se presiona hacia abajo y un evento brake_off cuando se suelta.
Todo esto está muy bien, hasta que tenga la situación en la que el automóvil se enciende con el pedal del freno presionado . Como la luz de freno nunca recibió un evento brake_on , permanecerá apagada, claramente una situación indeseable. Encender la luz de freno por defecto solo revierte la situación.
¿Qué podría hacerse para resolver este 'problema de estado inicial'?
EDITAR: Gracias por todas las respuestas. Mi pregunta no era sobre un auto real. En los automóviles resolvieron este problema enviando continuamente el estado, por lo tanto, no hay ningún problema de inicio en ese dominio. En mi dominio de software, esa solución usaría muchos ciclos de CPU innecesarios.
EDIT 2: además de la respuesta de @ gbjbaanb , voy por un sistema en el que:
- el hipotético pedal de freno, después de la inicialización, envía un evento con su estado, y
- la hipotética luz de freno, después de la inicialización, envía un evento solicitando un evento de estado desde el pedal del freno.
Con esta solución, no hay dependencias entre componentes, no hay condiciones de carrera, no hay colas de mensajes que se vuelvan obsoletas y no hay componentes 'maestros'.
fuente
initialize
) que contiene los datos necesarios del sensor.Respuestas:
Hay muchas formas de hacer esto, pero prefiero mantener un sistema basado en mensajes lo más desacoplado posible. Esto significa que el sistema en general no puede leer el estado de ningún componente, ni ningún componente lee el estado de ningún otro (ya que de esa manera se encuentran los espaguetis de las dependencias).
Entonces, si bien el sistema en ejecución se cuidará solo, necesitamos una forma de decirle a cada componente que se inicie, y ya tenemos tal cosa en el registro de componentes, es decir, al inicio, el sistema central debe informar a cada componente que es ahora registrado (o pedirá a cada componente que devuelva sus detalles para que pueda registrarse). Esta es la etapa en la que el componente puede realizar sus tareas de inicio y puede enviar mensajes como lo haría en una operación normal.
Entonces, el pedal del freno, cuando se inicia el encendido, recibiría un mensaje de registro / verificación de la administración del automóvil y devolvería no solo un mensaje de "Estoy aquí y trabajando", sino que luego verificaría su propio estado y enviará el mensajes para ese estado (por ejemplo, un mensaje de pedal pisado).
El problema se convierte en una dependencia de inicio, ya que si la luz de freno aún no está registrada, entonces no recibirá el mensaje, pero esto se resuelve fácilmente poniendo en cola todos estos mensajes hasta que el sistema central haya completado su rutina de inicio, registro y verificación .
El mayor beneficio es que no se requiere un código especial para manejar la inicialización, excepto que ya tiene que escribir (bueno, si su envío de mensajes para eventos de pedal de freno está en un controlador de pedal de freno, también deberá llamarlo en su inicialización , pero eso generalmente no es un problema a menos que haya escrito ese código fuertemente ligado a la lógica del controlador) y no haya interacción entre los componentes, excepto aquellos que ya se envían entre sí de manera normal. ¡Las arquitecturas que pasan mensajes son muy buenas por esto!
fuente
Puede tener un evento de inicialización que establezca los estados adecuadamente al cargar / iniciar. Esto puede ser deseable para sistemas o programas simples que no incluyen múltiples piezas de hardware, sin embargo, para sistemas más complicados con múltiples componentes físicos, ya que corre el mismo riesgo de no inicializar en absoluto, si se pierde o se pierde un evento de "freno" a lo largo de su comunicación sistema (por ejemplo, un sistema basado en CAN) puede inadvertidamente configurar su sistema hacia atrás como si lo iniciara con el freno presionado. Cuantos más controladores tenga, como un automóvil, mayor será la probabilidad de que se pierda algo.
Para tener esto en cuenta, puede hacer que la lógica "freno activado" envíe repetidamente eventos "freno activado". Quizás cada 1/100 de segundo o algo así. Su código que contiene el cerebro puede escuchar estos eventos y activar el "freno" mientras los recibe. Después de 1 / 10seg de no recibir señales de "freno activado", se activa un evento interno "freno_desconectado".
Diferentes eventos tendrán requisitos de tiempo considerablemente diferentes. En un automóvil, la luz de freno debe ser mucho más rápida que la luz de control de combustible (donde probablemente sea aceptable un retraso de varios segundos) u otros sistemas menos importantes.
La complejidad de su sistema físico determinará cuál de estos enfoques es más apropiado. Dado que su ejemplo es un vehículo, es probable que desee algo similar a este último.
De cualquier manera, con un sistema físico, NO desea confiar en que un solo evento se reciba / procese correctamente. Los microcontroladores conectados en un sistema en red a menudo tienen un tiempo de espera "Estoy vivo" por este motivo.
fuente
En este caso, no modelaría el freno como un simple encendido / apagado. Más bien, enviaría eventos de "presión de frenado". Por ejemplo, una presión de 0 indicaría apagado y una presión de 100 estaría completamente deprimida. El sistema (nodo) enviaría constantemente eventos de presión de rotura (a cierto intervalo) a los controladores según sea necesario.
Cuando se inicia el sistema, comenzará a recibir eventos de presión hasta que se apague.
fuente
Si su único medio de pasar información de estado es a través de eventos, entonces está en problemas. En cambio, debe ser capaz de ambos:
La luz de freno puede verse como un observador del pedal de freno. En otras palabras, el pedal del freno no sabe nada sobre la luz de freno y puede funcionar sin ella. (Esto significa que cualquier idea de que el pedal del freno envía de manera proactiva un evento de "estado inicial" a la luz de freno está mal concebida).
Al crear una instancia del sistema, la luz de freno se registra con el pedal del freno para recibir notificaciones de frenado, y también lee el estado actual del pedal del freno y se enciende o apaga.
Luego, las notificaciones de frenado se pueden implementar de tres maneras:
Prefiero el primer enfoque, lo que significa que al recibir la notificación, la luz de freno simplemente hará lo que ya sabe cómo hacer: leer el estado actual del pedal del freno y encenderse o apagarse.
fuente
En un sistema basado en eventos (que actualmente uso y amo), encuentro importante mantener las cosas lo más desacopladas posible. Entonces, con esa idea en mente, profundicemos.
Es importante tener un estado predeterminado. Su luz de freno tomaría el estado predeterminado de 'apagado' y su pedal de freno tomaría el estado predeterminado de 'arriba'. Cualquier cambio después de eso sería un evento.
Ahora para abordar su pregunta. Imagine que su pedal de freno se inicializa y presiona, el evento se dispara, pero todavía no hay luces de freno para recibir el evento. He encontrado que es más fácil separar la creación de los objetos (donde los oyentes de eventos se inicializarían) como un paso separado antes de inicializar cualquier lógica. Eso evitará cualquier condición de carrera como la has descrito.
También me resulta incómodo usar dos eventos diferentes para lo que efectivamente es lo mismo .
brake_off
ybrake_on
podría simplificarsee_brake
con un parámetrobool on
. Puede simplificar sus eventos de esta manera agregando datos de respaldo.fuente
Lo que necesita es un evento de difusión y bandejas de entrada de mensajes. Una transmisión es un mensaje que se publica a un número no especificado de oyentes. Un componente puede suscribirse a eventos de difusión, por lo que solo recibe eventos en los que está interesado. Esto proporciona desacoplamiento, ya que el remitente no necesita saber quiénes son los receptores. La tabla de suscripción debe configurarse estáticamente durante la instalación del componente (en lugar de cuando se inicializa). La bandeja de entrada es una parte del enrutador de mensajes que actúa como un búfer para contener mensajes cuando el componente de destino está fuera de línea.
El uso de facturas trae un problema, que es el tamaño de la bandeja de entrada. No desea que el sistema tenga que retener un número creciente de mensajes para componentes que nunca más estarán en línea. Esto es importante especialmente con sistemas embebidos con restricciones estrictas de memoria. Para superar el límite de tamaño de la bandeja de entrada, todos los mensajes emitidos deben seguir algunas reglas. Las reglas son:
El nombre de difusión debe declararse durante el tiempo de instalación del componente. Si un componente envía una segunda transmisión con el mismo nombre antes de que el receptor procese la anterior, la nueva transmisión sobrescribe la anterior. Ahora puede tener un límite de tamaño de bandeja de entrada estático, que puede garantizarse que nunca superará un determinado tamaño y puede calcularse previamente en función de las tablas de suscripción.
Finalmente, también necesita un archivo de difusión. El archivo de difusión es una tabla que contiene el último evento de cada nombre de difusión. Los nuevos componentes que se acaban de instalar tendrán su bandeja de entrada prepoblada con mensajes del archivo de difusión. Al igual que la bandeja de entrada de mensajes, el archivo de difusión también puede tener un tamaño estático.
Además, para lidiar con situaciones en las que el enrutador de mensajes está fuera de línea, también necesita bandejas de salida de mensajes. La bandeja de salida de mensajes es parte del componente que contiene el mensaje saliente temporalmente.
fuente