// public/docs-app/v1/doc/lru_mapa_unificacion_modos_universal_s182.md
// 2026-06-02T00:00:00Z
// TAG: hilo conductor

# LRU · Mapa · Unificacion a un solo sistema de modos (per-senal canonico)

> Audit de superficie + estadificacion del frente que cierra `T-S165`: dejar
> UN solo sistema de modos — el de la SENAL (ControlBus per-senal) — y retirar
> el **modo** per-hwObj (RouterService + `RouterMode` + gate `HW_PERMISSIONS`),
> **conservando el hwObj como agrupacion**. Articulado y reencuadrado en s182
> (Manuel & Claude Opus 4.8) tras cerrar R3 (modos per-senal: un eje, siempre
> con modo, sin Auto/origin). NO implementa: mapea, fija la decision marco y
> propone rebanadas reversibles para sesion(es) dedicada(s).

---

## 0 · Decision marco

- **D-s182-UNIF-1**: la SENAL es la fuente de verdad y mantiene su estado. El
  sistema per-senal (ControlBus `signalModes` + `SIGNAL_PERMISSIONS` +
  `signalCountdowns`) es el **canonico y unico**.
- **D-s182-UNIF-2**: nomenclatura unificada en los nombres per-senal. `Loc` y
  `Local` son lo mismo -> queda **`Local`**. Idem `HwReal`->`Real`,
  `Rnd`->`Gen`, `HwSim`->`HwSim`.
- **D-s182-UNIF-3** (reencuadre s182): el per-hwObj fundia DOS conceptos. Se
  separan:
  1. **Modo** -> propiedad de la SENAL (sistema unico). Se retira el modo
     per-hwObj (`RouterMode`, countdown->HwReal, gate 1).
  2. **Agrupacion** -> un *conjunto nombrado de senales*, **sin estado de modo
     propio**. El hwObj es una agrupacion (lente/selector); su criterio hoy es
     "sensores de una misma placa" pero es contingente (manana por sistema,
     ATA, display...). **Se conserva.** Las operaciones de grupo (cabecera,
     `Rnd`) son **fan-out** a `setSignalMode` por cada senal del conjunto; el
     grupo nunca guarda modo. El "— Mixto —" es la vista agregada.
- Sin transitorios que profundicen el dual-control. Cada rebanada deja un
  sistema mas limpio, no dos conviviendo mas.

---

## 1 · Correspondencia de vocabulario (4 <-> 4, limpia)

| per-hwObj (RouterMode) | fuente              | per-senal (SignalMode) | fuentes (`SIGNAL_PERMISSIONS`) |
|------------------------|---------------------|------------------------|--------------------------------|
| `Loc`                  | slider local        | **`Local`**            | `slider`                       |
| `HwReal` (default)     | hardware real       | **`Real`**             | `mqtt-hw`                      |
| `HwSim`                | slider via MQTT      | **`HwSim`**            | `mqtt-sim`                     |
| `Rnd`                  | generador/registry  | **`Gen`**              | `sim1`, `registry`             |

El mapeo es 1:1. El modo de la senal es, de hecho, **el selector de fuente**:
`Gen` = la genero (sim/SignalLab/Rnd), `Real` = la captura el hardware, `Local`
= a mano, `HwSim` = inyectada por MQTT. Es el conmutador virtual<->fisico de la
senal — corazon de la mision de la plataforma.

---

## 2 · Las dos familias de agrupacion (concepto, no scope)

- **Grupos logicos / funcionales**: `sim1_eng`, `sim1_bleed`, `sim1_press`,
  `sim1_airdata`, `sim1_nav`. Agrupan por sistema senales que **hoy generamos**
  virtualmente. Sin sustrato fisico, sin efecto fisico.
- **Grupos hardware**: `ble_A92418`, `ble_07bcf8`, `ble_A931D4`... (muchas y
  crecientes). Placas BLE que capturan senales del mundo fisico. Agrupan por
  sustrato (P12). **Van a sustituir** como fuente de las mismas senales que hoy
  alimentamos con `Gen`.

Ambas familias son la misma cosa — agrupaciones (lentes sin estado). Conectan
con la maquinaria de **facetas** ya existente (s157+, `queryFacets`, realm
aviation ata/systems): el hwObj seria *una faceta mas* — la faceta "sustrato"
(P12, alfiler enlaces-sustrato s162). **No es scope nuevo** (P11): se reconoce
el concepto, se mantiene la agrupacion-hardware tal cual, y se difiere lo
generico hasta que haya disparador concreto (alguien que quiera agrupar por ATA
de verdad).

---

## 3 · Inventario de la superficie per-hwObj

Confirmado por lectura directa (s182). **[sweep]** = requiere grep del arbol
antes de tocar (rebanada R0). Cada pieza cae en una de tres cajas:
**[MODO]** (se retira) · **[AGRUP]** (se conserva como agrupacion) ·
**[FISICO]** (se conserva como efecto fisico derivado).

### 3.1 · Motor — `src/simulation/RouterService.ts`
`setMode`/`setModeAll`/`getMode`/`reset`/`resetAll`/`getCountdown`/
`cancelCountdown`/`allowsSource` + countdown->`HwReal` **[MODO]**.
`register`/`unregister`/`getAll`/`subscribe`/`subscribeAll` (pertenencia)
**[AGRUP]** — pero la agrupacion ya vive en el mapa `hwObjs` + catalogo; el
RouterService como *registro* de pertenencia es redundante. `_handleInhibition`
(inhibicion firmware en HwSim, `_inhibitionSender`) **[FISICO]**.
`init({inhibitionSender})` (App.tsx **[sweep]**). `window.RouterServiceDebug`.

### 3.2 · Gate activo per-hwObj — `ControlBus.ts`
`HW_PERMISSIONS` + `allows`/`allowsChannel` (usa `rs.getMode`) **[MODO]**. Es el
gate 1 de los dos apilados en `DataPlane.writeBySensorId` (§4).

### 3.3 · Puente React — `src/simulation/RouterServiceReact.ts`
`useRouterMode`/`useRouterActions`/`useRouterCountdown`/`useRouterModeAll`
**[MODO]**.

### 3.4 · Handlers MQTT — `src/datosLru01/registryMqttHandlers.ts`
- `createSensorDataHandler`: gate per-hwObj `allows(vhwObj,mqttSource)`
  **[MODO]**; auto-registro fisico `rs.register` **[AGRUP]**; replica a slot de
  senal (sombra s172 R1); dispara monitor1 **[FISICO]**.
- `createRouter1Send1Handler`: ramifica por `hwObj.routerMode` **[MODO]**.

### 3.5 · ORC — `src/datosLru01/ObjectRegistryContext.tsx`
`updateHwObjSensors` (gate `allows(id,'registry')`) **[MODO]**;
`rs.subscribeAll`->sincroniza `hwObj.routerMode` **[MODO]**;
`rs.register`/`unregister` **[AGRUP]**; `enlazar` dispara monitor1 **[FISICO]**.

### 3.6 · UI — `ComponenteLru/index.tsx` (tarjeta del LruBar)
- `handleChangeSliderhw1Sw`: slider llama `rs.setMode('Loc')` + escribe
  `'slider'`; **no toca el modo per-senal** (origen de la regresion, §4)
  **[MODO]**.
- `handleModoChange`: `<select>` de cabecera -> `rs.setMode` **[MODO]**. Se
  recablea a **lote per-senal** (fan-out), no se elimina (peticion Manuel s182).
- `LocalCountdown` de cabecera (`useRouterCountdown`): **se retira** **[MODO]**.
- `LOC_COUNTDOWN_MS`.

### 3.7 · UI — `LruToolbar.tsx`
`Rnd` (`iniciarCountdownRandom`->`rs.setMode('Rnd')`) + `finalizarCountdown`
(`setModeAll('HwReal')`) + `useRouterCountdown` **[MODO]**. (El `<select>` de
lote global ya migro a per-senal en R2b.)

### 3.8 · Firmware — `src/datosLru01/monitor1.ts`  **[FISICO]**
`sendMonitoredCmd` -> MQTT `to/cfg {monitored:true,...}`. Ortogonal al modo:
ligado a enlace + fisico + subscribers. **Permanece** (no es del sistema de
modos). Es propiedad de "este grupo esta respaldado por hardware real".

### 3.9 · Tipo — `src/datosLru01/types.ts`
`RouterMode`, `DEFAULT_ROUTER_MODE` **[MODO]**. Consumidores **[sweep]**.

### 3.10 · Inventario completo de consumidores (R0 sweep s182)

Grep de `src\` (excluido el motor). Categorias:

- **Init / DI** (cablea el sistema; se retira en R5): `App.tsx`
  (`initRouterService` con `commsSend` como `inhibitionSender`),
  `simulation/routerServiceInit.ts`, `simulation/controlBusInit.ts` (inyecta
  `rs.getMode` en ControlBus = arma el gate 1).
- **Tipo**: `datosLru01/types.ts` (`RouterMode`, `ROUTER_MODES`,
  `DEFAULT_ROUTER_MODE='HwReal'`, campo `routerMode` en HwObj, `ROUTER_LABELS`).
  Tipo `RouterMode` **duplicado local** en
  `pages/Sim1/hooks/useIB04Registry.ts:83` (no importa de types).
  `types/glossary.ts`.
- **Gate 1**: `simulation/ControlBus.ts` (`HW_PERMISSIONS`, `getModeFn`,
  `allows`/`allowsChannel`, `getPermissionTable`, `logBlocked`).
- **UI con control de modo per-hwObj** (recablear a lote per-senal / fan-out, o
  retirar):
  - `simulation/RouterModeChip.tsx` — chip global de modo (usado en
    `pages/Sim1/Sim1Header.tsx`). `setMode`/`setModeAll`/`useRouterModeAll`/
    `useRouterCountdown`.
  - `components/EnlacesPanel/EnlacesPanelContent.tsx` + `hw/HwObjPopup.tsx` —
    modo per-hwObj en el panel de enlaces (toggle `HwReal`/`HwSim`).
  - `datosLru01/Hw1AdminPanel/tabs/TabControl.tsx` (cambia modo) +
    `TabStatus.tsx` + `hw1Shared.tsx` (display de `routerMode`).
  - `pages/Col4/Detalle01.tsx` — modo global + `Rnd` + countdown.
  - `datosLru01/ComponenteLru/index.tsx` (cabecera + slider + LocalCountdown).
  - `components/panels/LruBar/LruToolbar.tsx` (`Rnd`).
  - `pages/Sim1/Sim1.tsx` (`rs.setMode('Rnd')`).
- **Generador Rnd (datos)**: `simulation/SimService.ts` (~998-1007) itera hwObjs
  con `routerMode==='Rnd'` y escribe al DataPlane (source `registry`). Es el
  PRODUCTOR de Rnd -> clave para `Rnd`->`Gen` (R3).
- **Indicador online derivado**: `pages/Sim1/hooks/useIB04Registry.ts` suscribe
  `routerMode` -> `ControlOnline1` rojo/verde por instrumento (Online =
  Rnd|HwSim|HwReal; Offline = Loc o sin enlace). Necesita equivalente per-senal.
- **Sync / pertenencia**: `ObjectRegistryContext.tsx` (`subscribeAll`->
  `hwObj.routerMode`; `register`/`unregister`/`resetAll`).
- **router1Send1**: `registryMqttHandlers.ts` (ramas por `routerMode`) +
  `ObjectRegistryContext.tsx` (listener) + `comms/adapters/MqttAdapter.ts`
  (comentario).
- **Codigo muerto**: `allowsSource` — 0 consumidores fuera del motor. Borrable
  ya o en R5.

**Hallazgo**: la superficie es mayor que la lista confirmada inicial. R3 (lote
per-senal) abarca varias UIs (RouterModeChip, EnlacesPanel, Hw1AdminPanel,
Detalle01, Sim1), no solo el LruBar. R5 crece en consecuencia.

---

## 4 · Los dos gates apilados + la regresion del slider (s182)

`DataPlane.writeBySensorId` aplica DOS gates en serie:

```
// gate 1 (per-hwObj):  if (!cb.allows(hwObjId, source)) return;
// gate 2 (per-senal):  if (sm !== undefined && sm !== 'Gen'
//                          && !cb.allowsSignal(sensorId, source)) return;
```

El gate 2 (activo desde s167, selectivo) muerde toda senal con modo != `Gen`
(Real/Local/HwSim) si la fuente no esta permitida. `Gen` y mode-less pasan.

**Regresion T-S182-SLIDER-LRUBAR** (introducida por R3.1):
- Antes de R3.1 las senales hw nacian sin modo -> gate 2 las dejaba pasar -> el
  slider del LruBar (`'slider'`) escribia.
- R3.1 las siembra en `Real` -> gate 2 corta `'slider'` (Real solo admite
  `mqtt-hw`). El slider del LruBar solo pone el eje per-hwObj a `Loc` (pasa gate
  1) pero deja la senal en `Real` -> **bloqueado en gate 2**. Valor no llega al
  DataPlane; Mixer no lo refleja; sin countdown per-senal.
- El slider del **Mixer** funciona porque hace `setSignalMode('Local')` antes ->
  gate 2 pasa.

Implicacion: el slider no se arregla "limpio" en aislado. Migrarlo a per-senal
hace pasar gate 2, pero entonces gate 1 bloquea (routerMode != `Loc`). Hay que
**retirar el gate 1** a la vez — y eso acopla con `Rnd`/`registry` (§5). Frente,
no parche.

---

## 5 · Asimetrias — disueltas por el reencuadre (D-s182-UNIF-3)

### 5.1 · per-device vs per-signal -> NO es asimetria
La cabecera de tarjeta y el `Rnd` no son "un modo de grupo": son **operaciones
de grupo que hacen fan-out** a `setSignalMode` por senal. El grupo no guarda
estado. No hay nada que centralizar. El dropdown de agrupacion **se conserva**
como control de lote.

### 5.2 · Inhibicion de firmware -> NO es asimetria del sistema de modos
Es un **efecto fisico de la agrupacion-hardware en concreto**, derivado de los
modos per-senal de sus senales: "inhibir el dispositivo X si [alguna|todas] sus
senales esta en `HwSim`". Vive en la capa de la agrupacion-hardware observando
los modos de sus senales. Otras agrupaciones (sistema, ATA) no arrastran efecto
fisico. Decision semantica abierta (D-s182-FW).

### 5.3 · Gate 1 -> gate 2: que cambia al retirar el modo per-hwObj
Al quitar el gate 1, el gate 2 queda unico. Cobertura: `mqtt-hw` sobre `Real`
OK; `slider` sobre `Local` OK; `registry`/`sim1` sobre `Gen` OK; `Gen`/mode-less
**totalmente permisivos** (aceptar -> D-s182-GEN). `Rnd` servia `registry` a
senales hw via gate 1; sin gate 1, el generador necesita que la senal este en
`Gen` => el boton `Rnd` pasa a poner las senales del grupo en `Gen` (acopla
retirada de gate 1 con migracion de `Rnd`).

### 5.4 · Countdown universal de la senal (matiz Manuel s182)
El countdown es propiedad de la SENAL, no del control. Maquinaria ya existe,
keyed por `signalId` (`signalCountdowns` + `useSignalCountdown`). El anillo
(`CountdownRing` + candado) hoy vive en `MixerRow`; al tener 2do consumidor
(LruBar) se **extrae a lego compartido** (patron R2a). En el LruBar: el
`LocalCountdown` de **cabecera** (per-hwObj) **se retira**; cada slider monta un
anillo **por-sensor** leyendo `useSignalCountdown(signalId)` de su senal.
Cualquier anillo de la misma senal late sincronizado solo.

---

## 6 · Donde aterriza cada funcionalidad per-hwObj

| Pieza per-hwObj | Destino |
|-----------------|---------|
| `Loc` (slider drag) | slider hace `setSignalMode('Local')` + `startSignalCountdown` |
| `<select>` cabecera tarjeta | **se conserva** como lote per-senal (fan-out sobre el grupo) |
| `Rnd` (boton) | lote per-senal -> `Gen` sobre el grupo |
| countdown -> `HwReal` | countdown per-senal -> `Real` (ya existe) |
| `LocalCountdown` cabecera (anillo per-hwObj) | **se retira** |
| anillo por-sensor en el LruBar | **se anade** (lego compartido, `useSignalCountdown`) |
| gate 1 (`allows`/`HW_PERMISSIONS`) | retirar; gate 2 unico |
| inhibicion firmware `HwSim` | efecto fisico derivado de modos per-senal del grupo-hardware (D-s182-FW) |
| `monitor1` | permanece (ortogonal al modo) |
| `register`/`getAll` (pertenencia) | la agrupacion ya vive en mapa `hwObjs`/catalogo; el registro RouterService es redundante |
| `router1Send1` ramas por routerMode | reevaluar / migrar a per-senal |
| `RouterMode` tipo + `useRouter*` | retirar tras migrar consumidores |

---

## 7 · Decisiones abiertas (arbitrar en su rebanada)

- **D-s182-FW** · inhibicion de firmware al derivar HwSim del grupo-hardware:
  "alguna senal en HwSim" vs "todas". Sin cerrar.
- **D-s182-GEN** · al retirar gate 1, `Gen`/mode-less quedan permisivos de
  facto. Aceptar (coherente con gate selectivo) o endurecer. Sin cerrar.
- **D-s182-RND** · alcance de `Rnd`->`Gen` (todas las senales hw del grupo?
  incluye no enlazadas?). Sin cerrar.
- **D-s182-ORDEN** · orden de rebanadas (§8): la regresion del slider no cierra
  "limpia" hasta retirar gate 1 + migrar Rnd. Aceptar que persiste hasta esa
  rebanada (decision tomada: limpio sobre transitorio) o adelantarla.

---

## 8 · Estadificacion borrador (reversible · sujeta a §7)

- **R0 · sweep + este mapa**: grep completo de `getRouterService`/`useRouter*`/
  `RouterMode`/`HW_PERMISSIONS`/`allowsSource`/`rs.setMode`. Completa [sweep].
- **R1 · lego del anillo de countdown**: extraer `CountdownRing` (+candado) de
  `MixerRow` a `components/panels/`, leyendo `useSignalCountdown`. Aditivo, sin
  cambio de comportamiento.
- **R2 · slider del LruBar a per-senal + retirar gate 1**: `handleChangeSliderhw1Sw`
  -> `setSignalMode('Local')` + `startSignalCountdown` + anillo por-sensor;
  retirar gate 1 de `writeBySensorId` + handlers; retirar `LocalCountdown` de
  cabecera. **Cierra T-S182-SLIDER-LRUBAR.** Acopla D-s182-GEN. Riesgo medio.
- **R3 · Rnd + cabecera de tarjeta a lote per-senal (fan-out)**: `Rnd`->`Gen`
  sobre el grupo; cabecera conservada como lote per-senal.
- **R4 · inhibicion de firmware**: capa de derivacion (efecto fisico del
  grupo-hardware desde modos per-senal). D-s182-FW.
- **R5 · retirada del modo per-hwObj**: eliminar `RouterService`/
  `RouterServiceReact`/`RouterMode`/`HW_PERMISSIONS`/`allowsSource`/ramas
  `router1Send1`. El hwObj permanece como agrupacion (mapa `hwObjs`/catalogo).
  `tsc` como red. Cierre del frente.

Orden y fusiones sujetos a arbitraje (D-s182-ORDEN).

---

## 9 · Tensiones

- **T-S165-UNIFICACION-ARBITRAJE** · per-senal: R0..R3 (modos) hechos. Queda
  ESTE frente (retirada del modo per-hwObj) — su ultima parte.
- **T-S182-SLIDER-LRUBAR** · regresion: slider del LruBar bloqueado por gate 2
  sobre senales hw en `Real` (R3.1). Cierra en R2.
- **T-S182-ENLACE-SENAL-SENAL** (nueva · horizonte): las senales virtual y
  fisica son **ambas ciudadanas de primer orden**. Un enlace futuro senal->senal
  con conversion (p.ej. `ble_07bcf8/adc1` binario crudo -> `N1_L` convierte a
  unidades) las acoplaria. **No existe aun; diferido.** Conecta con el alfiler
  dos universos de nomenclatura (s151) y Realm v3 (T-S67-A1). Hoy la pertenencia
  al grupo-hardware esta codificada en el `signalId` (`{hwObjId}/{port}`); una
  agrupacion generica querria la pertenencia como *relacion*, no incrustada en
  la identidad — tambien horizonte (P11).
- **D-s182-UNIF-1/2/3** · per-senal canonico; nomenclatura unificada en `Local`;
  separar modo (senal) / agrupacion (lente sin estado) / efecto fisico (grupo
  hardware).

---

*`lru_mapa_unificacion_modos_universal_s182.md` · hilo conductor · un solo
sistema de modos (per-senal canonico) · se retira el MODO per-hwObj, se conserva
el hwObj como AGRUPACION (faceta sustrato) · firmware = efecto fisico derivado ·
vocabulario 1:1 (Loc->Local, HwReal->Real, Rnd->Gen, HwSim=HwSim) · dos gates
apilados en writeBySensorId · regresion T-S182-SLIDER-LRUBAR (R3.1 + gate 2) ·
dropdown de grupo se conserva como lote (fan-out) · anillo por-sensor en el
LruBar, cabecera se retira · enlace senal-senal = horizonte (T-S182-ENLACE) ·
estadificacion borrador R0..R5 · D-s182-FW/GEN/RND/ORDEN abiertas*
