fix(uart): el enganche de serie sobrevive a recrear el simulador

El fan-out del Interconnect envuelve el onSerialData de la INSTANCIA y
la marca con un flag. Compilar, resetear o cambiar de motor crea una
instancia nueva: sin flag y sin envoltorio, asi que la placa dejaba de
oirse por el cable a mitad de sesion. Sintoma real: la Pi en modo Linux
encendia el LED del Arduino (Pi -> Uno), pero la respuesta del Arduino
no volvia nunca (Uno -> Pi), porque el Uno habia recreado su simulador
al compilar despues de que se construyeran las rutas.

Se vuelve a enganchar en cada simulatorMap.set (siete puntos: AVR,
RP2040, RISC-V, ESP32, STM32 y los shims).
This commit is contained in:
David Montero Crespo 2026-07-29 20:55:05 +02:00
parent 3f867f7234
commit a0ba1fbd28
2 changed files with 40 additions and 1 deletions

View File

@ -756,7 +756,11 @@ export function notifyBoardReady(_boardId: string, wires: readonly Wire[]): void
*/
export function reensureSerialHooks(boardId: string): void {
const entry = boards.get(boardId);
if (entry && entry.serialFanout.size > 0) ensureSerialHook(entry);
if (!entry || entry.serialFanout.size === 0) return;
// The hook lives on the sim/bridge INSTANCE and marks it with a flag.
// A fresh instance carries no flag, so ensureSerialHook installs on it;
// this call is what makes that happen after a compile or a reconnect.
ensureSerialHook(entry);
}
/** For tests: reset all internal state. */

View File

@ -1215,6 +1215,11 @@ export const useSimulatorStore = create<SimulatorState>((set, get) => {
try { existingShim.clearAllProxies(); } catch { /* ignore */ }
}
simulatorMap.set(id, shim);
// The Interconnect's serial fan-out wraps the INSTANCE's
// onSerialData; a fresh simulator (compile, reset, engine
// swap) drops that wrapper, so a wired peer stopped
// hearing this board mid-session. Re-ensure it here.
icReensureSerialHooks(id);
}
const initialSim = createSimulator(
@ -1382,6 +1387,11 @@ export const useSimulatorStore = create<SimulatorState>((set, get) => {
// Shim so PartSimulationRegistry parts (I2C displays, sensors, SPI
// panels) attach to this STM32 the same way they do on ESP32.
simulatorMap.set(id, new Stm32BridgeShim(bridge, pm));
// The Interconnect's serial fan-out wraps the INSTANCE's
// onSerialData; a fresh simulator (compile, reset, engine
// swap) drops that wrapper, so a wired peer stopped
// hearing this board mid-session. Re-ensure it here.
icReensureSerialHooks(id);
} else {
const sim = createSimulator(
boardKind,
@ -1400,6 +1410,11 @@ export const useSimulatorStore = create<SimulatorState>((set, get) => {
);
// Cross-board routing now handled by Interconnect (see bind below).
simulatorMap.set(id, sim);
// The Interconnect's serial fan-out wraps the INSTANCE's
// onSerialData; a fresh simulator (compile, reset, engine
// swap) drops that wrapper, so a wired peer stopped
// hearing this board mid-session. Re-ensure it here.
icReensureSerialHooks(id);
// ── Attach a PIO bus peripheral if a factory supports this board.
// The pro overlay registers a CYW43 WiFi peripheral for 'pi-pico-w'
@ -2400,6 +2415,11 @@ export const useSimulatorStore = create<SimulatorState>((set, get) => {
const shim = new Esp32BridgeShim(bridge, pm);
shim.onSerialData = serialCallback;
simulatorMap.set(boardId, shim);
// The Interconnect's serial fan-out wraps the INSTANCE's
// onSerialData; a fresh simulator (compile, reset, engine
// swap) drops that wrapper, so a wired peer stopped
// hearing this board mid-session. Re-ensure it here.
icReensureSerialHooks(boardId);
set((s) => ({
boardType: type,
@ -2434,6 +2454,11 @@ export const useSimulatorStore = create<SimulatorState>((set, get) => {
getOscilloscopeCallback(boardId),
);
simulatorMap.set(boardId, sim);
// The Interconnect's serial fan-out wraps the INSTANCE's
// onSerialData; a fresh simulator (compile, reset, engine
// swap) drops that wrapper, so a wired peer stopped
// hearing this board mid-session. Re-ensure it here.
icReensureSerialHooks(boardId);
set((s) => ({
boardType: type,
@ -2513,6 +2538,11 @@ export const useSimulatorStore = create<SimulatorState>((set, get) => {
const shim = new Esp32BridgeShim(bridge, pm);
shim.onSerialData = serialCallback;
simulatorMap.set(boardId, shim);
// The Interconnect's serial fan-out wraps the INSTANCE's
// onSerialData; a fresh simulator (compile, reset, engine
// swap) drops that wrapper, so a wired peer stopped
// hearing this board mid-session. Re-ensure it here.
icReensureSerialHooks(boardId);
set({ simulator: shim as any, serialOutput: '', serialBaudRate: 0 });
} else {
const sim = createSimulator(
@ -2529,6 +2559,11 @@ export const useSimulatorStore = create<SimulatorState>((set, get) => {
getOscilloscopeCallback(boardId),
);
simulatorMap.set(boardId, sim);
// The Interconnect's serial fan-out wraps the INSTANCE's
// onSerialData; a fresh simulator (compile, reset, engine
// swap) drops that wrapper, so a wired peer stopped
// hearing this board mid-session. Re-ensure it here.
icReensureSerialHooks(boardId);
set({ simulator: sim, serialOutput: '', serialBaudRate: 0 });
}
console.log(`Simulator initialized: ${boardType}`);