Sim1 Bridge Architecture — Conectar simulación con instrumentos

2026-04-01 · Documento de diseño y planificación
Cómo hacer que los sensores simulados muevan las agujas de los instrumentos Animate

Por qué — El objetivo

Visión

Un alumno abre Sim1, carga un instrumento ECAM en el Viewer, pulsa Play, y ve las agujas moviéndose con los datos de la simulación. Puede inyectar fallos y ver cómo responden los indicadores. Puede cambiar de escenario y los instrumentos se adaptan. Todo en tiempo real, sin hardware físico, sin configuración manual.

Qué queremos conseguir

Sim1 ya genera datos de sensores de aviación (EGT, N1, Oil Pressure, IAS, etc.) a 60fps con escenarios y fallos inyectables. Los instrumentos Animate (ECAM, PFD, altímetro, ILS, etc.) ya existen y funcionan con hardware real en Col4. El objetivo es conectar ambos mundos: que los datos simulados alimenten los instrumentos existentes sin modificar los clips ni el motor de simulación.

Por qué es importante

Sin esta conexión, Sim1 es un generador de datos sin visualización rica. Los strip charts muestran curvas, pero un alumno entiende mejor un flame-out viendo la aguja de EGT dispararse en un ECAM real que mirando una línea en un gráfico. La experiencia visual inmersiva es el objetivo de la plataforma.

Principio rector

"Los instrumentos de medida son los ojos virtuales que extienden nuestros sentidos y nos ayudan a percibir dimensiones desconocidas." — El alumno que ve las agujas moverse desarrolla intuición física que ningún manual puede dar.

Cómo funciona hoy — El flujo existente en Col4

Flujo Col4 — Hardware real → Agujas (funcional) ESP32 MQTT events Registry events handler DOM dispatch ble_A92418_online Bridge_03 _processSensor change1() Aguja se mueve 1. Clip Animate se carga y define cfg1: cfg1 = { swObj:"Ils1", hwObj:"ble_A92418", "loc":{hw1:{puerto:"adc1",...}, cfg1:{...}} } 2. Bridge_03 escucha eventos DOM: addEventListener("ble_A92418_online", handler) channelMap: { loc:"adc1", gs:"adc2" } 3. Cuando llega MQTT, el Registry hace dispatch _online con los valores: CustomEvent("ble_A92418_online", { detail: { message: '{"adc1":2048,"adc2":3100}' } }) Bridge lee channelMap: loc→adc1→datos["adc1"]=2048 → parent["loc"].change1(2048)

Puntos clave del flujo actual

cfg1 — Configuración hardcodeada en cada clip

Cada instrumento Animate define un cfg1 dentro de su fichero JS con: la identidad del instrumento (swObj), el hwObj por defecto (hwObj), y un mapa de canales donde cada aguja se asocia a un puerto del hardware (loc→adc1, gs→adc2, etc.). El Bridge lee este cfg1 al inicializarse.

InstrumentBridge_03 — El puente entre datos y agujas

Constructor: new InstrumentBridge_03(clip). Se auto-configura desde clip.parent.cfg1. Agrupa canales por hwObj efectivo. Crea listeners DOM para cada hwObj ({hwObjId}_online y {hwObjId}_local). Cuando recibe un evento, itera el channelMap y llama parent[aguja].change1(valor) para cada aguja.

API en caliente: bridge.enlazar(hwObjId, channelMap) y bridge.desenlazar(hwObjId). Un Bridge puede estar enlazado a múltiples hwObjs simultáneamente (N:M).

Evento DOM _online — El transporte de datos

Cuando el Registry recibe datos MQTT (events), actualiza el hwObj en el state React y dispara un CustomEvent DOM con nombre {hwObjId}_online. El payload es un JSON con { puerto: valor } mapeado según el cfg1 del hwObj. Este dispatch solo ocurre dentro del handler de events MQTT — no en updateHwObjSensors.

Rangos ADC — Todo el sistema trabaja en 0–4095

Los clips Animate esperan valores en rango ADC de 12 bits (0–4095). El firmware del ESP32 envía valores crudos de ADC. El cfg1 define rangos de entrada/salida (valMin1In/valMax1InvalMin1Out/valMax1Out) pero en la práctica casi todos tienen 0–4095 en ambos lados. La función change1() de cada aguja del clip acepta este rango y lo convierte internamente en rotación/posición visual.

Qué falta — Los dos eslabones rotos

Flujo Sim1 — Dos eslabones rotos (×) useSimState 60fps RAF useSimEngine 100ms interval Registry sim1_eng ✓ No dispatch _online ausente Bridge apunta a ble_A92418 Eslabón 1: updateHwObjSensors no dispara _online Solo actualiza React state — los clips nunca se enteran Eslabón 2: cfg1 apunta a ble_A92418, no sim1_eng Bridge escucha el hwObj equivocado + puertos ADC ≠ sensores Sim1

Eslabón 1 — updateHwObjSensors no dispara _online

Dónde: ObjectRegistryContext.tsx línea 420. La función updateHwObjSensors(id, values) solo hace setHwObjs(prev => ...) — actualiza el state React (para que window.RegistryDebug.getHwObjs() muestre los valores), pero no dispara ningún evento DOM. Los clips Animate escuchan eventos DOM, no cambios de React state.
Contraste con events MQTT: Cuando llega un mensaje MQTT, el handler de events (línea 1250+) sí dispara document.dispatchEvent(new CustomEvent(hwObjId + '_online', ...)). Esa lógica de dispatch no existe en updateHwObjSensors.

Eslabón 2 — cfg1 hardcodeado y rangos incompatibles

hwObj equivocado: Los clips apuntan a hwObj: "ble_A92418" o "ble_07BCF8" (hardware real). El Bridge escucha ble_A92418_online. Pero los hwObjs virtuales de Sim1 se llaman sim1_eng, sim1_bleed, etc. Aunque se arregle el eslabón 1, sim1_eng_online no llega al Bridge porque escucha otro nombre.
Puertos incompatibles: Los canales del clip usan puertos ADC genéricos (adc1, adc2...) pero los sensores de Sim1 se llaman EGT_L, N1_L, OilP_L. El channelMap { loc: "adc1" } no encontraría EGT_L en el payload de sim1_eng_online.
Rangos incompatibles: Los clips esperan 0–4095 (ADC 12 bits). Sim1 genera valores en rango real: EGT 200–950°C, N1 0–110%, IAS 0–400 kt. Un valor de 612°C llegaría al clip como 612 cuando espera 0–4095. La aguja apenas se movería.

Inventario de instrumentos disponibles

Todos son instrumentos de test modificables. Todos usan InstrumentBridge_03. Todos esperan rango ADC 0–4095.

FicheroswObjsCanales (agujas)hwObj actual
ecam1_eng_01.jsEng_psi1, Eng_psi2, Eng_oil1, Eng_oil2psi1, oil1, txt1 (×4)ble_07BCF8 / ble_A92418
ecam1_apu_01.jsApu_N1, Apu_EGT + gen paneln1, egt1, v1, hz1, psi1, gen1, txt1/txt2ble_A92418 / ble_07BCF8
pfd_01.jsPfd1VSpeed1, Pfd1HSpeed1vSpeed1, speed1, txt1ble_A92418 / ble_07BCF8
ils_01.jsIls1loc, gs, flagNav, flagGsble_A92418
altimetro_01.jsTurn2, Rumbo1, Alt1baston, bola, rumbo1, alt1/alt2, hg, mb, bug, ref1ble_A92418 / ble_0BC4BC
machAirSpeed_01.jsMachAirSpeed1speed1, mach, knots, vmo, bug, pointer1–4ble_A92418
cabina_01.jsIls1, Rumbo1, VertSpeed1, Hsi3, Alt1, AirSpeed1(compilación de los anteriores)ble_A92418 / ble_0BC4BC

Patrón común en todos los clips

_this.cfg1 = {
  swObj: "NombreInstrumento",       // identidad
  hwObj: "ble_XXXXXX",              // hwObj por defecto (hardware real)
  "aguja1": {
    hw1:  { puerto:"adc1", puertoValor:0, canal:"aguja1", hwObj:"----" },
    cfg1: { tipo:"adc", bits:12, valMin1In:0, valMax1In:4095,
            valMin1Out:0, valMax1Out:4095,
            canalMin1In:0, canalMax1In:100,
            canalMin1Out:0, canalMax1Out:100 }
  },
  // ... más agujas
};
var bridge = new InstrumentBridge_03(_this);

Arquitectura propuesta — 4 capas

Arquitectura 4 capas — Sim1 → Agujas Capa 1 — Dispatch _online (Registry) updateHwObjSensors dispara CustomEvent({hwObjId}_online) con valores actuales. Infraestructura invisible. Una vez. ~30 min Capa 2 — Mapeo declarativo (sim-bridges.ts) Fichero que define: artículo → { swObj → hwObjId virtual, channelMap con escalado de rango real→ADC }. Extensible. ~1h Capa 3 — Auto-enlace + reasignación (SimBridgeConnector) Hook/componente que detecta carga de clip, busca mapeo, reasigna Bridges en caliente via enlazar/desenlazar. Escala valores. ~2h Capa 4 — Panel de conmutación Sim1 (SimEnlacesPanel) UI tipo ep panel: filas=agujas del clip, columnas=sensores Sim1. Drag para conectar. Preview de rango. Presets guardables. sesión aparte Cada capa depende de la anterior · Se puede detener en cualquier punto · La capa 1 ya permite test manual desde consola Capa 1+2+3 = auto-enlace funcional sin UI · Capa 4 = control total del usuario

Detalle de cada capa

Capa 1 — Dispatch _online desde updateHwObjSensors

Qué

Modificar updateHwObjSensors en ObjectRegistryContext.tsx para que, después de actualizar el state React, dispare un evento DOM {hwObjId}_online con los valores actualizados. Mismo formato que el dispatch del handler de events MQTT.

Por qué

Es el eslabón roto #1. Sin este dispatch, ningún Bridge de ningún clip Animate puede recibir datos de hwObjs actualizados via updateHwObjSensors — solo reciben datos que llegan por MQTT.

Cómo

Añadir el dispatch dentro del callback de setHwObjs, después de confirmar que hubo cambios (changed = true). Usar el mismo formato de payload que el handler de events: { detail: { message: JSON.stringify({ puerto: valor, ... }) } }.

Dónde

src/datosLru01/ObjectRegistryContext.tsx, función updateHwObjSensors (línea ~420).

Cuándo

Primer paso. Prerequisito para todo lo demás.

Riesgo

Bajo. El dispatch es un evento DOM fire-and-forget. Si nadie escucha, no pasa nada. No afecta al flujo MQTT existente. La frecuencia es ~10fps (100ms interval de useSimEngine) con 18 hwObjs = 180 dispatches/s — menor que el RAF a 60fps que ya corre.

Test de verificación

Con un clip cargado en Col4 (cabina_01 con Bridge enlazado a ble_A92418), ejecutar desde consola:

// Esto debería mover las agujas si la Capa 1 funciona
document.dispatchEvent(new CustomEvent('ble_A92418_online', {
  detail: { message: JSON.stringify({ adc1: 2000, adc2: 3000, adc3: 1000 }) }
}));

Si las agujas se mueven, el dispatch funciona. Después se verifica que updateHwObjSensors genera el mismo efecto automáticamente.

Capa 2 — Mapeo declarativo (sim-bridges.ts)

Qué

Un fichero TypeScript que define cómo conectar sensores del catálogo de Sim1 con canales de cada clip Animate. Independiente del catálogo y de Firebase.

Por qué

Los sensores de Sim1 se llaman EGT_L (rango 200–950°C) y los puertos del clip se llaman adc1 (rango 0–4095). Necesitamos un mapeo explícito que diga: "la aguja psi1 del clip ECAM se alimenta del sensor OilP_L del sistema sim1_eng, escalando de 0–100 psi a 0–4095 ADC".

Cómo

// src/simulation/sim-bridges.ts
export interface SimBridgeMapping {
  swObj: string;           // nombre del instrumento en el clip
  hwObjId: string;         // hwObj virtual de Sim1 (sim1_eng, sim1_airdata, etc.)
  channels: Record<string, {
    sensorId: string;      // sensor del catálogo Sim1 (EGT_L, N1_L, etc.)
    inMin: number;         // rango entrada (valor real mínimo)
    inMax: number;         // rango entrada (valor real máximo)
    outMin?: number;       // rango salida (default 0)
    outMax?: number;       // rango salida (default 4095)
  }>;
}

export interface SimBridgeConfig {
  articuloRef: string;     // ID del artículo/animación en Datos01
  mappings: SimBridgeMapping[];
}

export const SIM_BRIDGE_CONFIGS: SimBridgeConfig[] = [
  {
    articuloRef: 'ecam1_eng_01',
    mappings: [
      {
        swObj: 'Eng_psi1',
        hwObjId: 'sim1_eng',
        channels: {
          psi1: { sensorId: 'OilP_L', inMin: 0, inMax: 100 },
          txt1: { sensorId: 'OilP_L', inMin: 0, inMax: 100 },
        }
      },
      // ... más instrumentos del clip
    ]
  },
  // ... más artículos
];

Dónde

src/simulation/sim-bridges.ts — nuevo fichero junto a sim-catalog.ts.

Cuándo

Después de la Capa 1. Se empieza con un mapping para un clip sencillo (ecam1_eng_01 o machAirSpeed_01) y se va ampliando.

Decisión de diseño: ¿por qué fichero separado?

Tres candidatos se consideraron: incluirlo en sim-catalog.ts (acopla catálogo con clips), en el artículo de Firebase (requiere migración de datos), o en fichero separado. El fichero separado es independiente, fácil de iterar, y se puede migrar a Firebase en fase 4 via useSimCatalog.

Capa 3 — Auto-enlace y reasignación (SimBridgeConnector)

Qué

Un hook (useSimBridgeConnector) o componente que vive dentro de Sim1 y orquesta la conexión automática cuando se carga un clip en el Viewer.

Por qué

Sin esto, el usuario tendría que reasignar Bridges manualmente desde la consola. El auto-enlace hace que cargar un instrumento en Sim1 "simplemente funcione" si hay un mapeo definido.

Cómo — Flujo del auto-enlace

1. El usuario carga un clip en el InstrumentsPanel (ej: ecam1_eng_01).
2. El Bridge_03 se inicializa con su cfg1 hardcodeado (hwObj: ble_07BCF8).
3. useSimBridgeConnector detecta que hay Bridges registrados (via InstrumentBridge_03.registry).
4. Busca el artículo actual en SIM_BRIDGE_CONFIGS.
5. Si hay mapeo, para cada mapping:
   a. bridge.desenlazar("ble_07BCF8") — quita el enlace al hardware real
   b. bridge.enlazar("sim1_eng", { psi1: "OilP_L" }) — conecta al hwObj virtual
6. El dispatch de updateHwObjSensors (Capa 1) envía sim1_eng_online con { OilP_L: 42 }.
7. El Bridge recibe el evento, busca datos["OilP_L"] en el channelMap, llama parent["psi1"].change1(42).

Problema del escalado de rango

El valor 42 (42 psi) llega al clip que espera 0–4095. Hay dos opciones:

Opción A: Escalar en el dispatch — useSimEngine o el connector escalan los valores antes de dispatch. Pro: Bridge no se modifica. Contra: los valores en el Registry dejan de ser reales.

Opción B: Escalar en el connector — un wrapper que intercepta el evento sim1_eng_online, escala los valores según el mapping, y re-dispatcha con valores ADC. Pro: Registry mantiene valores reales. Contra: un paso extra.

Opción C (recomendada): Modificar el cfg1 del clip en caliente después de enlazar. Cuando el connector reasigna un Bridge, también actualiza clip.parent.cfg1[aguja].cfg1.valMin1In y valMax1In con el rango real del sensor. Así el clip acepta valores reales y los mapea internamente. Pro: limpio, el clip se adapta. Contra: depende de que change1() del clip use los rangos del cfg1 (hay que verificar).

Dónde

src/pages/Sim1/hooks/useSimBridgeConnector.ts — nuevo hook usado en InstrumentsPanel o Sim1.tsx.

Cuándo

Después de Capa 1 y Capa 2. Es la primera pieza que el usuario ve funcionar.

Capa 4 — Panel de conmutación Sim1 (SimEnlacesPanel)

Qué

Una UI dentro de Sim1 (como panel del SimGrid o como popup flotante) que muestra una matriz de conmutación donde el usuario puede conectar/desconectar sensores de Sim1 con agujas de clips manualmente.

Por qué

El auto-enlace (Capa 3) funciona cuando hay un mapeo predefinido. Pero el usuario puede querer experimentar: conectar EGT a la aguja del altímetro para ver cómo se comporta, o conectar un sensor de un sistema diferente al que el clip fue diseñado. El panel de conmutación da control total.

Cómo — Diseño de la UI

Filas: Agujas del clip cargado (psi1, oil1, n1, speed1...). Se detectan automáticamente desde InstrumentBridge_03.registry consultando los channelMaps de cada Bridge.

Columnas: Sensores disponibles en Sim1, agrupados por sistema (ENG: EGT_L, N1_L, OilP_L... | BLEED: BldP_L, BldT_L... | etc.). Se leen del catálogo sim-catalog.ts.

Celdas: Click en celda = toggle enlace. Celda activa muestra el rango de escalado con mini-barra visual. Solo un sensor por aguja (exclusividad). Al cambiar, se llama bridge.desenlazar del anterior y bridge.enlazar del nuevo.

Presets: Guardar/cargar configuraciones de enlace. "ECAM motores" = preset con psi1→OilP_L, oil1→OilT_L. Se almacenan en localStorage fase 1, Firestore fase 4.

Patrón EP Panel: Reutilizar el diseño visual del EnlacesPanel de Col4 (ep_panel, ep_matrix, ep_cell_editor) pero adaptado: las fuentes no son hwObjs físicos sino sensores simulados, y los destinos no son swObjs genéricos sino agujas específicas del clip cargado.

Dónde

Nuevo panel del SimGrid: src/pages/Sim1/panels/SimEnlacesPanel/. Se registra en registry.ts como panel #10. También accesible como popup flotante desde el header de Sim1 (botón ⇌).

Cuándo

Sesión separada. Las capas 1-3 deben funcionar primero — el panel las usa como infraestructura.

Flujo de datos completo (con las 4 capas)

Flujo completo Sim1 → Agujas (objetivo) useSimState RAF 60fps valuesRef EGT_L: 612 useSimEngine 100ms push Registry + dispatch Capa 1: _online event Bridge_03 Capa 3 change1 Aguja ✓ Escalado de rango (Capa 2 + 3): Sensor OilP_L = 42 psi (rango real 0–100) → escalar → 42/100 × 4095 = 1720 ADC → change1(1720) El mapping en sim-bridges.ts define inMin/inMax por sensor. El connector escala antes de que llegue al Bridge. Reasignación en caliente (Capa 3): bridge.desenlazar("ble_A92418") // deja de escuchar hardware real bridge.enlazar("sim1_eng", { psi1: "OilP_L" }) // escucha simulación Panel de conmutación (Capa 4): Matriz interactiva: filas = agujas del clip | columnas = sensores Sim1 por sistema | click = toggle enlace Presets guardables: "ECAM motores", "PFD completo", "Dashboard custom". localStorage → Firestore fase 4.

Problema del escalado de rangos

Los clips esperan 0–4095. Sim1 genera valores reales. Tres opciones evaluadas:

OpciónDónde escalaProContraVeredicto
A — En el dispatchuseSimEngine escala valores antes de pushToRegistryBridge no se modificaRegistry tiene valores falsos (ADC en vez de reales)descartada
B — En el connectorWrapper intercepta valores, escala real→ADC, dispatcha con valores ADCRegistry mantiene valores realesEscalado explícito por sensorelegida ✓
C — En el cfg1 del clipConnector modifica cfg1.valMin1In/valMax1In en calienteLimpio, el clip se adaptachange1() captura rangos en closure — NO se re-leedescartada ✓ verificado

Decisión tomada — Verificado en código

Se analizó la implementación real de change1() en los clips. El patrón es idéntico en todos:

// En frame_0 (se ejecuta UNA VEZ al crear el clip):
var VValMin1 = _this.parent.cfg1[_this.name].cfg1.valMin1In;  // captura
var VValMax1 = _this.parent.cfg1[_this.name].cfg1.valMax1In;  // captura
var VValRango = VValMax1 - VValMin1;
var VAngMin1 = -130;  // ángulo mínimo de la aguja (hardcodeado por clip)
var VAngMax1 = 70;    // ángulo máximo (hardcodeado por clip)
var VAngRango = VAngMax1 - VAngMin1;

this.change1 = function processEventAguja(VValor1) {
  var porcentaje = (VValor1 - VValMin1) / VValRango;       // normaliza 0–1
  var agujaRotac = (porcentaje * VAngRango) + VAngMin1;     // mapea a ángulo
  _this.rotation = agujaRotac;                              // mueve la aguja
};

Hallazgo clave: change1() SÍ usa los rangos del cfg1 (valMin1In, valMax1In), pero los captura en closure durante frame_0 (inicialización del clip). Modificar cfg1 después no tiene efecto — los valores ya están capturados en variables locales inmutables.

Consecuencia: La opción C está descartada para clips existentes. La opción B es la correcta: el connector escala los valores de rango real a rango ADC (0–4095) antes de que lleguen al Bridge. Como todos los clips tienen valMin1In:0, valMax1In:4095, el escalado es simple: valorADC = ((valorReal - sensorMin) / (sensorMax - sensorMin)) * 4095.

Mejora futura (Fase 5): En clips nuevos diseñados para Sim1, change1() leerá rangos directamente de cfg1 en cada llamada (no en closure), permitiendo cambio de rango en caliente sin escalar fuera.

Decisiones de diseño

#PreguntaDecisiónRazón
1¿Dónde vive el mapeo?Fichero separado sim-bridges.tsIndependiente del catálogo y Firebase. Fácil de iterar. Migrable a Firestore en fase 4.
2¿Quién ejecuta la reasignación?Hook useSimBridgeConnector en Sim1Detecta carga de clip y reasigna automáticamente. Separación de responsabilidades.
3¿Cómo se escalan los rangos?Opción B (connector escala antes de dispatch)Verificado: change1() captura rangos en closure durante frame_0. Modificar cfg1 en caliente no tiene efecto. Escalar fuera es la única opción para clips existentes.
4¿Panel de conmutación?Sí — SimEnlacesPanel como panel #10 del SimGridControl total del usuario. Patrón ep panel adaptado.
5¿Auto-enlace o solo manual?Auto-enlace con override manualExperiencia fluida por defecto. Panel para experimentar.
6¿Modificar clips existentes?No en fase 1 — reasignar Bridges en calienteLos clips son de test, pero la reasignación en caliente es más limpia que editar JS.
7¿Composiciones nuevas para Sim1?Fase futura (opción C del plan original)Crear clips con cfg1 apuntando directamente a sim1_* y rangos reales. Cuando la infraestructura esté probada.
8¿Topic events2 o events?events — events2 está obsoletoMarcado en s7. El handler interno se sigue llamando events2 por legacy pero el topic MQTT es events.

Fases de implementación

Fase 1 — Fontanería (esta sesión o la siguiente)

1.1 Capa 1: Añadir dispatch _online en updateHwObjSensors. ~30 min. Fichero: ObjectRegistryContext.tsx.
1.2 Test manual: Verificar con cabina_01 en Col4 que un dispatch manual mueve las agujas. 5 min.
1.3 Test wire-up: Con Sim1 corriendo, verificar que updateHwObjSensors genera dispatches automáticos. 5 min.

Fase 2 — Primer instrumento funcional

2.1 Capa 2: Crear sim-bridges.ts con mapeo para ecam1_eng_01 (el más relevante). ~1h.
2.2 Capa 3: Implementar useSimBridgeConnector con auto-enlace básico. ~2h.
2.3 Verificar: Cargar ecam1_eng_01 en Sim1 Viewer, pulsar Play, ver agujas moverse. Si no se mueven, investigar escalado.
2.4 Resolver escalado: Probar opción C (modificar cfg1 en caliente). Si no funciona, implementar opción B.

Fase 3 — Ampliar cobertura

3.1: Añadir mappings para todos los clips: pfd_01, ils_01, altimetro_01, machAirSpeed_01, ecam1_apu_01.
3.2: Probar con cabina_01 (multi-instrumento). Verificar que la reasignación de múltiples Bridges funciona.
3.3: Refinar auto-enlace: detección inteligente por nombre de swObj cuando no hay mapeo explícito.

Fase 4 — Panel de conmutación (sesión aparte)

4.1: Diseñar UI del SimEnlacesPanel. Filas = agujas, columnas = sensores, agrupados por sistema.
4.2: Implementar como panel #10 del SimGrid + popup flotante desde header.
4.3: Presets de enlace guardables en localStorage.
4.4: Integrar con el auto-enlace: si hay preset guardado, usarlo; si no, auto-enlace; si no, panel vacío.

Fase 5 — Evolución de clips Animate (futuro)

5.1: Crear clips Animate nuevos con cfg1 apuntando a sim1_* y rangos reales.
5.2: ECAM completo con 11 páginas, PFD con actitud/velocidad/altitud.
5.3: Estos clips no necesitan reasignación — funcionan directamente con Sim1.

Visión Fase 5+ — Evolución del patrón de instrumentos

Los clips actuales tienen limitaciones heredadas del diseño original para hardware real. Las fases futuras pueden abordar mejoras en múltiples dimensiones sin rehacer las simulaciones desde cero:

Flexibilidad — change1() dinámico: Reemplazar la closure inmutable por una función que lea rangos de cfg1 en cada llamada: change1(valor) { var min = _this.parent.cfg1[_this.name].cfg1.valMin1In; ... }. Esto permite cambiar rangos en caliente sin recrear el clip. Un instrumento podría mostrar EGT (0–950°C) y después Oil Temp (0–200°C) sin recargar.

Ancho de banda — API batch: En vez de llamar change1() por cada aguja individualmente (N llamadas por tick), definir un changeBatch({ psi1: 42, oil1: 87, txt1: 42 }) a nivel de instrumento que actualice todas las agujas en un solo paso. Reduce el overhead de la iteración del channelMap en el Bridge.

Usabilidad — Auto-detección de canales: Que el clip exponga un manifiesto de sus canales disponibles: instrument.getChannels() → [{ name:"psi1", type:"gauge", angMin:-130, angMax:70, label:"Oil Pressure" }]. El SimEnlacesPanel los mostraría automáticamente sin necesidad de mapeo manual. Plug-and-play.

Estandarización — Interfaz ISimInstrument: Definir un contrato estándar que todos los clips implementen: init(cfg), change(channelValues), getChannels(), setRange(channel, min, max), destroy(). Los clips legacy se adaptan con un wrapper; los nuevos lo implementan nativamente. El Bridge habla con la interfaz, no con el clip directamente.

Repetibilidad — Clips parametrizados: Un solo fichero JS para "gauge genérico" que acepta parámetros de configuración: número de agujas, escala, colores, ángulos, labels, marcas de umbral. En vez de 7 ficheros de gauges diferentes, un único gauge_generic_01.js con { agujas: 2, escala: "lineal", angMin: -130, angMax: 70, marcas: [{valor: 850, color: "amber"}, ...] }. Se configura desde sim-bridges.ts o desde el SimEnlacesPanel.

Facilidad de cambios — Hot-reload de cfg1: Un mecanismo para que cuando el usuario cambia un mapping en el SimEnlacesPanel, el clip se reconfigure sin recargar: actualizar channelMap + rangos + labels dinámicamente. Requiere que el clip soporte setRange() y setLabel() en tiempo real.

Rendimiento — SharedArrayBuffer para datos: En lugar de eventos DOM (serializar JSON → dispatch → parse), los clips podrían leer directamente de un buffer compartido (el Sim1Bus Float64Array descrito en arquitectura de buses). Zero-copy, zero-allocation. El clip solo necesita saber su offset en el buffer.

Portabilidad — Clips sin InstrumentBridge: Clips que se conectan directamente al Registry via un adaptador React ligero, sin depender de InstrumentBridge_03 ni eventos DOM. Un <SimInstrument src="gauge_generic_01" channels={{psi1: "OilP_L"}} /> que internamente usa refs y RAF para leer valores del Registry.

Ficheros involucrados

Ficheros a modificar

FicheroRutaCambioCapa
ObjectRegistryContext.tsxsrc/datosLru01/Dispatch _online en updateHwObjSensors1
InstrumentsPanel.tsxsrc/pages/Sim1/panels/InstrumentsPanel/Montar useSimBridgeConnector3

Ficheros nuevos

FicheroRutaFunciónCapa
sim-bridges.tssrc/simulation/Mapeo declarativo artículo → sensores → canales2
useSimBridgeConnector.tssrc/pages/Sim1/hooks/Auto-enlace y reasignación de Bridges3
SimEnlacesPanel/src/pages/Sim1/panels/Panel de conmutación visual4

Ficheros de referencia (no modificar)

FicheroPor qué es relevante
InstrumentBridge_03.jsAPI del Bridge: enlazar/desenlazar en caliente, channelMap, _processSensorData
sim-catalog.tsCatálogo de sensores con rangos reales (min/max/cruise)
useSimEngine.tsPush al Registry cada 100ms — genera los datos que dispararán _online
cabina_01.js + otros clipsPatrones de cfg1, swObj, channelMaps. Referencia para crear mappings.

LRU Platform — Sim1 Bridge Architecture
2026-04-01 · Sesión s7 · Claude Opus 4
Traspaso s7 · SimGrid v3 · Simulación · Buses · Estado técnico