EnlacesPanel v2 — Documentacion del componente

Componente reutilizable de gestion de enlaces swObj-hwObj
Refactorizado en s12 (2026-04-04) — modular, autocontenido, documentado

1. Vision y proposito

El EnlacesPanel gestiona los enlaces entre instrumentos de software (swObjs) y dispositivos de hardware (hwObjs). Permite crear, editar y eliminar enlaces N:M con exclusividad atomica por canal, sincronizacion en tiempo real con InstrumentBridge_03, y persistencia de configuraciones via escenarios.

Principio de diseno: El componente lee datos del RegistryContext y EscenariosContext directamente — no necesita props de datos. Los hwObjs pueden ser fisicos (ble_07BCF8) o virtuales (sim1_eng) — al componente le da igual.
Estado actual (s12): Componente 100% autocontenido en src/components/EnlacesPanel/. Refactorizado de un monolito de 587 lineas a 8 modulos con responsabilidad unica. Funciona en Col4 (panel flotante) y Sim1 (slot PanelGrid) simultaneamente.

2. Arquitectura post-refactor

src/components/EnlacesPanel/
├── index.ts                     barrel: exports Content + Float + types
├── types.ts                     EnlacesPanelContentProps + re-exports tipos
├── helpers.ts                   buildDefaultChannelMap, findExistingLink, getBridgeForSwObj
├── useBridgeSync.ts             hook: enlazar/desenlazar con sync Bridge DOM
├── EnlacesPanelContent.tsx      orquestador (~195 lin) — compose secciones
├── MatrixCell.tsx               celda interactiva con editor channelMap
├── EscenariosSection.tsx        CRUD escenarios con estado propio
├── ObjectsSection.tsx           lista colapsable swObjs/hwObjs
├── epc.css                      root .epc_root + header
├── EnlacesPanel.css             estilos matriz + celdas + editor + objetos
├── EnlacesPanel_escenarios.css  estilos seccion escenarios
│
├── hw/                          sub-componentes de hardware fisico
│   ├── useEspSensorControl.ts   hook MQTT timer/Auto-Delta ESP32
│   ├── EspTimerSelect.tsx       selector intervalo + boton config
│   ├── EspTimerSelect.css
│   ├── AutoDeltaPanel.tsx       panel histeresis Auto-Delta
│   ├── AutoDeltaPanel.css
│   ├── HwObjPopup.tsx           popup 3 tabs (estado/control/sensores)
│   └── HwObjPopup.css
│
└── float/                       wrapper flotante Col4
    ├── index.tsx                 position:fixed, drag, resize, minimizar
    └── EnlacesPanelFloat.css

2.1 Separacion de responsabilidades

CapaResponsabilidadFichero(s)
TiposProps del componente y re-exports de tipos del dominio (ObjId, ChannelMap, etc.)types.ts
HelpersFunciones puras sin dependencia React: channel maps, bridge accesshelpers.ts
HooksSincronizacion RegistryContext con InstrumentBridge_03 DOMuseBridgeSync.ts
OrquestadorContextos, compose matriz + secciones + popup. Sin logica de negocio propiaEnlacesPanelContent.tsx
MatrizCelda interactiva: enlazar/desenlazar, editor channelMap, exclusividad atomicaMatrixCell.tsx
EscenariosCRUD completo: guardar, cargar, importar/exportar JSON, edicion inline, escenario inicialEscenariosSection.tsx
ObjetosLista presentacional de hwObjs y swObjs registradosObjectsSection.tsx
HWControl de hardware fisico: timer ESP, Auto-Delta, popup 3 tabs. Solo para hwObjs fisicoshw/*.tsx
FloatContenedor flotante Col4: position:fixed, drag, resize, minimizarfloat/index.tsx

2.2 Flujo de datos

RegistryContext ──→ EnlacesPanelContent (orquestador)
                      ├── MatrixCell × N (lee hwObjs/swObjs/enlaces)
                      │     └── onEnlazar/onDesenlazar → useBridgeSync
                      │           ├── RegistryContext.actions.enlazar()
                      │           └── InstrumentBridge_03.enlazar() (DOM sync)
                      ├── EscenariosSection (lee EscenariosContext directo)
                      │     └── guardar/cargar/importar/exportar
                      └── ObjectsSection (presentacional, solo props)

EscenariosContext ──→ EscenariosSection (acceso directo via useEscenarios)
HwObjsContext ────→ EnlacesPanelContent → handleModoChange → HwObjPopup

3. Inventario de ficheros

FicheroLineasFuncion
index.ts5Barrel: EnlacesPanelContent, EnlacesPanelContentProps, EnlacesPanel (float)
types.ts23Props + re-exports de ObjId, HwObjsMap, SwObjsMap, MatrizEnlaces, ChannelMap
helpers.ts70getBridgeForSwObj, buildDefaultChannelMap, findExistingLink
useBridgeSync.ts65Hook: handleEnlazar, handleDesenlazar, handleUpdateChannelMap
EnlacesPanelContent.tsx195Orquestador: contextos, matriz, secciones, HwObjPopup, modoChange
MatrixCell.tsx175Celda: click enlazar, editor channelMap, exclusividad atomica, AutoDeltaPanel
EscenariosSection.tsx175CRUD escenarios: formulario guardar, lista, edicion inline, import/export
ObjectsSection.tsx65Lista colapsable hwObjs + swObjs con badges de tipo y contadores
epc.css46Root .epc_root + header .epc_header
EnlacesPanel.css617Matriz, celdas, editor, botones, objetos, resize
EnlacesPanel_escenarios.css~230Seccion escenarios: save, items, badges, acciones

3.1 Sub-componentes HW

FicheroLineasFuncion
hw/useEspSensorControl.ts~260Hook MQTT: timer, Auto-Delta, sensor histeresis, status/capabilities
hw/EspTimerSelect.tsx~95Select intervalo + dot estado + boton config Auto-Delta
hw/AutoDeltaPanel.tsx~240Panel config histeresis: modo pct/abs, heartbeat, apply
hw/HwObjPopup.tsx~500Popup 3 tabs: Estado, Control (router+sim+timer), Sensores (tabla)

3.2 Wrapper flotante

FicheroLineasFuncion
float/index.tsx180Contenedor position:fixed. Drag por titlebar, resize por grip, minimizar. Monta <EnlacesPanelContent hideHeader />
float/EnlacesPanelFloat.css123Estilos: .ep_panel, .ep_titlebar, .ep_resize_grip

4. EnlacesPanelContent (orquestador)

Fichero principal (~195 lineas). No contiene logica de negocio propia — delega a sub-componentes y hooks. Su responsabilidad es:

4.1 Props

PropTipoDefaultUso
classNamestring?Clase CSS adicional en el root
styleCSSProperties?Estilos inline en el root
hideHeaderboolean?falseOculta el header compacto (cuando el contenedor ya tiene titlebar)

5. MatrixCell

Celda interactiva de la matriz swObj x hwObj. Cada celda muestra el estado del enlace y permite editarlo.

5.1 Estados visuales

EstadoClase CSSVisual
Sin enlace.ep_cell.empty+
Enlazado activo.ep_cell.linked🔗 2/3
Enlazado inactivo.ep_cell.linked.inactive⏸ 0/3
Expandido (editor).ep_cell.linked.expandedEditor channelMap visible

5.2 Editor de channelMap

Al hacer click en una celda enlazada, se expande el editor que muestra cada mapping canal → puerto con selects. Incluye:

6. EscenariosSection

Seccion colapsable que gestiona escenarios de enlaces. Accede directamente a useEscenarios() — estado propio de UI, sin props de datos.

6.1 Operaciones

OperacionUIAccion
GuardarInput nombre + descripcion + botonactions.guardarEscenario(nombre, desc)
CargarBoton por itemactions.cargarEscenario(id)
Actualizar🔄 (confirm)actions.actualizarEscenario(id)
RenombrarDouble-click → edit inlineactions.renombrarEscenario(id, nombre, desc)
Escenario inicial☆/⭐ toggleactions.setEscenarioInicial(id|null)
Exportar📤 botonactions.exportarEscenario(id) → descarga JSON
Importar📥 + file inputactions.importarEscenario(json)
Eliminar🗑️ (confirm)actions.eliminarEscenario(id)
Modo libre🔓 botonactions.desactivarEscenario()

6.2 Escenarios vs Scenarios

Escenarios (EscenariosContext) = que esta conectado a que (enlaces swObj-hwObj).

Scenarios (Sim1/sim-catalog) = como se comportan los sensores (fisica de vuelo).

Son complementarios, no sustitutos. Un escenario de enlaces funciona con cualquier scenario de simulacion.

7. ObjectsSection

Componente presentacional. Recibe datos por props (hwObjs, swObjs, enlaces, ids). Muestra dos listas colapsables con badges de tipo y contadores.

Cada item muestra: badge tipo (physical/virtual/animate/component), nombre, y metadata (puertos, subs, enlaces, emitsAs).

8. useBridgeSync

Hook que envuelve las acciones del RegistryContext con sincronizacion DOM. Cada operacion hace dos cosas:

8.1 API

HandlerParamsLogica extra
handleEnlazarsw, hw, mapDesenlaza hwObjs huerfanos del bridge antes de enlazar el nuevo
handleDesenlazarsw, hwDirecta
handleUpdateChannelMapsw, hw, mapRe-enlaza con el nuevo map (sin cleanup)

9. Helpers

Funciones puras sin dependencia de React. Fichero helpers.ts.

FuncionUso
getBridgeForSwObj(swObj)Acceso tipado a window.InstrumentBridge_03.registry. Retorna la instancia o null. Centraliza el cast (window as any)
buildDefaultChannelMap(sw, hw, ...)Genera channelMap por defecto al enlazar. Respeta exclusividad: si el swObj ya tiene enlaces, todos los canales van inactivos
findExistingLink(sw, canal, hw, enlaces)Busca si un canal ya tiene enlace activo en otro hwObj. Devuelve el hwObjId del conflicto o null

10. Sub-componentes HW

Subdirectorio hw/ — controles especificos de hardware fisico (ESP32). Se ocultan automaticamente para hwObjs virtuales.

10.1 useEspSensorControl

Hook central de comunicacion MQTT con el ESP. Gestiona timer periodico, modo Auto-Delta, histeresis por sensor, y capabilities. Importado por EspTimerSelect y AutoDeltaPanel.

10.2 EspTimerSelect

Selector compacto: dot de estado (coloreado segun modo) + select de intervalo + boton ⚙ que abre AutoDeltaPanel en modo global.

10.3 AutoDeltaPanel

Panel de configuracion de histeresis Auto-Delta. Dos modos: global (desde EspTimerSelect) y sensor (desde MatrixCell editor). Toggle pct/abs, selector umbral, heartbeat.

10.4 HwObjPopup

Panel flotante draggable con 3 tabs. Position:fixed, z-index 1300.

TabContenido
📊 EstadoTipo, router, conexion, Rx/Tx, simInhibited, uptime, sensores
⚙️ ControlGrid botones router, toggle sim/HwReal, config publicacion (timer/autoΔ/stop)
🔬 SensoresTabla puertos: tipo (chip), valor, rango, barra 3px
Para hwObjs virtuales: El popup funciona pero los controles de hardware (reboot, sim toggle, timer config) no aplican. Pendiente: filtrar por hwObj.type === 'virtual' y mostrar info de sistema.

11. Wrapper flotante (Col4)

Subdirectorio float/ — contenedor position:fixed para el panel de enlaces en la vista Col4 (Detalle01).

Monta <EnlacesPanelContent hideHeader /> con su propio titlebar.

Importado por Detalle01.tsx via el barrel: import { EnlacesPanel } from '../../components/EnlacesPanel'

12. Estructura CSS

12.1 Prefijos de clases

PrefijoFicheroAmbito
.epc_*epc.cssRoot y header del componente
.ep_*EnlacesPanel.cssMatriz, celdas, editor, botones, objetos
.ep_esc_*EnlacesPanel_escenarios.cssSeccion escenarios
.ep_panel, .ep_titlebar*float/EnlacesPanelFloat.cssSolo contenedor flotante Col4
.esp_timer_*hw/EspTimerSelect.cssSelector timer ESP
.adp_*hw/AutoDeltaPanel.cssPanel Auto-Delta
.hwp_*hw/HwObjPopup.cssPopup hwObj

12.2 Temas

El componente usa colores hardcoded oscuros (#1a1a2e, #16213e, #3a3a5c) heredados del diseno original. No usa variables --sim1-*. Funciona con temas oscuros pero puede contrastar con el tema Daylight.

13. Contextos React consumidos

ContextoConsumido porQue lee
RegistryContextEnlacesPanelContenthwObjs, swObjs, enlaces, actions
HwObjsContextEnlacesPanelContentsetHwObjs para handleModoChange
EscenariosContextEscenariosSectionescenarios, escenarioActivoId, escenarioInicialId, actions
Regla: El componente NUNCA importa ViewportLayoutContext — eso es responsabilidad del contenedor flotante (float/index.tsx).

14. Como montar el componente

14.1 Ejemplo minimo

import { EnlacesPanelContent } from '@/components/EnlacesPanel';

function MyPanel() {
  return (
    <div style={{ width: 400, height: 600 }}>
      <EnlacesPanelContent />
    </div>
  );
}

Requisitos: RegistryProvider, EscenariosProvider y HwObjsContext.Provider mas arriba en el arbol React (ya estan en App.tsx).

14.2 Con header oculto (contenedor con titlebar propio)

<EnlacesPanelContent hideHeader style={{ flex: '1 1 auto', minHeight: 0 }} />

14.3 Como panel del PanelGrid

// En registry.ts:
const EnlacesPanelW = lazy(() => import('./EnlacesPanelWrapper'));

{ id: 'sim-enlaces', label: 'Enlaces', icon: '⚡',
  component: EnlacesPanelW, minSize: { cols: 1, rows: 2 },
  accepts: ['bridges', 'enlaces', 'sensors'] }

14.4 Como panel flotante en Col4

import { EnlacesPanel } from '../../components/EnlacesPanel';

<EnlacesPanel visible={showEnlacesPanel} onClose={() => setShowEnlacesPanel(false)} />
LRU Platform — EnlacesPanel v2 — 2026-04-04 — Refactorizado en s12: 8 modulos, 3 subdirectorios, ~800 lineas componente + ~850 lineas hw