Saltar a contenido

Núcleo de simulación

El núcleo mantiene un mapa de señales en unidades de ingeniería y lo avanza en ticks. No sabe nada de Modbus, de UDP ni de WebSocket: los adaptadores se enchufan encima y traducen en el borde.

Estado

El estado es un objeto mutable único. No hay copias, no hay réplicas, no hay una segunda fuente de verdad. Esta es la razón de D-02 y de D-01.

Cada señal tiene:

Campo Significado
value Valor actual en unidades de ingeniería
mode auto o forced (D-08)
shadow Valor que produciría el bloque si no estuviera forzada
quality Válido, no inicializado, o fuera de rango
t_us Instante del último cambio, reloj monótono

shadow existe por los bloques con memoria y no se expone en la interfaz.

Los dos lazos

graph TD
    T[Lazo de tick · 50 ms] --> E[Aplicar flancos pendientes]
    E --> B[Evaluar bloques y expresiones]
    B --> S[Escribir estado]
    A[Lazo de ejes · 10 ms] --> I[Integrar posicion y velocidad]
    I --> U[Emitir paquete UDP]
    S -.consignas.-> A
    S -.snapshot.-> W[Emisor de telemetria · 10 Hz]
    S -.eventos.-> L[Buffer de log]

Lazo de tick, 50 ms. Aplica los flancos de comando recibidos desde el último tick, evalúa el grafo de bloques y expresiones en orden topológico, y escribe el estado. Nada más. Ni envía telemetría, ni escribe en disco, ni evalúa nada que pueda bloquear.

Lazo de ejes, 10 ms. Integra posición a partir de la velocidad de consigna y emite el paquete UDP. Está aislado porque a 50 ms el movimiento entre muestras es del orden del espaciado de rayos, demasiado grueso para que el controlador cierre un lazo de posición con suavidad (D-10).

Lo que nunca ocurre dentro del tick

Regla dura

El tick no escribe en disco, no envía por socket y no espera a nadie. Cualquier trabajo que pueda bloquear va en un camino desacoplado.

better-sqlite3 es síncrono: escribir el registro de eventos dentro del lazo introduciría jitter en la medida que el banco existe para tomar. Los eventos se acumulan en un buffer en memoria y se vuelcan en lote fuera del tick.

Lo mismo con la telemetría: un emisor independiente muestrea el estado a 10 Hz y publica, descartando lo que no llegue a tiempo (D-12).

Orden de evaluación

El orden lo fija el grafo de dependencias, no el orden del fichero de configuración. Se calcula una vez al cargar, por ordenación topológica.

Un ciclo es un error de carga, no un comportamiento oscilante. La configuración se rechaza con el ciclo concreto en el mensaje de error. Aceptar ciclos con un valor retardado un tick sería posible pero produciría un modelo cuyo comportamiento depende del orden de declaración, y eso es exactamente lo que hace irreproducible una prueba.

Deriva del lazo

Los dos lazos se programan contra el reloj monótono y corrigen deriva acumulada, no encadenan setTimeout de periodo fijo. La desviación real de la cadencia respecto a la nominal se contabiliza y se expone como métrica de sesión: es lo que permite responder, cuando aparece un fallo de temporización, si el problema fue del controlador o del banco.

Si el lazo se retrasa más de un periodo completo, se salta el tick perdido en vez de acumularlo. Ejecutar dos ticks seguidos para ponerse al día produciría un salto de integración en los ejes y un doble consumo de flancos.

Interfaz del núcleo hacia los adaptadores

El núcleo expone una superficie deliberadamente pequeña:

Operación Quién la usa
read(signal) adaptador Modbus, emisor UDP, telemetría
write(signal, value) adaptador Modbus, en escrituras del controlador
markEdge(signal) adaptador Modbus, en comandos por flanco
force(signal, value, actor) interfaz de operación
release(signal, actor) interfaz de operación
snapshot() emisor de telemetría

Que force y release lleven actor no es opcional: con la demo compartiendo estado, un forzado sin autor convierte el registro en algo indefendible.

Lo que la fase 1 necesita del núcleo

Solo el mapa de señales, los modos, y read/write/force/release. Sin bloques, sin expresiones, sin lazo de ejes. Una configuración con signals completo y blocks vacío debe cargar y servir. Esa es la razón de que la configuración esté separada en tres partes (D-06).