fix(boards): el overlay ya no secuestra el dibujo de las Raspberry Pi
Dos fallos que salieron probando pi-to-arduino-led-control y pi5-pir-motion-alarm. 1) BoardOnCanvas mira getProBoard() ANTES del switch OSS para decidir si dibuja un elemento del overlay. El overlay registraba un def minimo para las seis Pi solo para llevar una linea de setup del guest, y eso basto para cambiarles el render: en vez de la ilustracion Raspberry_Pi_3_illustration.svg salia la caja esquematica, con otras coordenadas de pines, y los cables quedaban colgando en la esquina. Ahora hay un registro aparte, registerGuestSetup(kind, linea), que lleva la cadena y nada mas; getGuestSetup() la resuelve dando prioridad al def del overlay si existe. 2) Las partes de entrada (PIR, botones, sensores) avisan con simulator.setPinState(pin, nivel). En una placa QEMU-Linux no hay simulador de MCU -- el CPU es el guest -- asi que la llamada acababa en la instancia AVR heredada y se perdia: pulsar el sensor no hacia nada. traceDetailed devuelve ahora tambien la placa a la que llega el pin, y si es de la familia Pi la parte recibe un simulador que empuja el nivel al bridge (gpio_in para el guest, el valor pin<N> que leen los shims del motor de navegador) y al PinManager de esa placa.
This commit is contained in:
parent
5a79836fdc
commit
8e1001740a
|
|
@ -13,12 +13,17 @@
|
||||||
|
|
||||||
import React, { useRef, useEffect, useCallback } from 'react';
|
import React, { useRef, useEffect, useCallback } from 'react';
|
||||||
import type { ComponentMetadata } from '../types/component-metadata';
|
import type { ComponentMetadata } from '../types/component-metadata';
|
||||||
import { useSimulatorStore } from '../store/useSimulatorStore';
|
import {
|
||||||
|
useSimulatorStore,
|
||||||
|
getBoardBridge,
|
||||||
|
getBoardPinManager,
|
||||||
|
} from '../store/useSimulatorStore';
|
||||||
import { useElectricalStore } from '../store/useElectricalStore';
|
import { useElectricalStore } from '../store/useElectricalStore';
|
||||||
import { useEditorStore } from '../store/useEditorStore';
|
import { useEditorStore } from '../store/useEditorStore';
|
||||||
import { buildProjectSdImage, decodeSdFiles } from '../utils/sdCardFiles';
|
import { buildProjectSdImage, decodeSdFiles } from '../utils/sdCardFiles';
|
||||||
import { PartSimulationRegistry } from '../simulation/parts';
|
import { PartSimulationRegistry } from '../simulation/parts';
|
||||||
import { isBoardComponent, boardPinToNumber } from '../utils/boardPinMapping';
|
import { isBoardComponent, boardPinToNumber } from '../utils/boardPinMapping';
|
||||||
|
import { isPiBoardKind } from '../types/board';
|
||||||
import { isKeyBindable, formatKeyLabel } from '../utils/keyButtonBindings';
|
import { isKeyBindable, formatKeyLabel } from '../utils/keyButtonBindings';
|
||||||
import {
|
import {
|
||||||
createDefaultPinResolver,
|
createDefaultPinResolver,
|
||||||
|
|
@ -128,7 +133,7 @@ export function traceDetailed(
|
||||||
fromPin: string,
|
fromPin: string,
|
||||||
depth: number,
|
depth: number,
|
||||||
activeSeen = false,
|
activeSeen = false,
|
||||||
): { arduinoPin: number | null; crossedActiveDevice: boolean } {
|
): { arduinoPin: number | null; crossedActiveDevice: boolean; boardId?: string } {
|
||||||
if (depth > 6) return { arduinoPin: null, crossedActiveDevice: activeSeen };
|
if (depth > 6) return { arduinoPin: null, crossedActiveDevice: activeSeen };
|
||||||
|
|
||||||
const wires = state.wires.filter(
|
const wires = state.wires.filter(
|
||||||
|
|
@ -157,7 +162,15 @@ export function traceDetailed(
|
||||||
if (boardEp || isBoardComponent(otherEp.componentId)) {
|
if (boardEp || isBoardComponent(otherEp.componentId)) {
|
||||||
const boardKind = boardEp?.boardKind ?? otherEp.componentId;
|
const boardKind = boardEp?.boardKind ?? otherEp.componentId;
|
||||||
const pin = boardPinToNumber(boardKind, otherEp.pinName);
|
const pin = boardPinToNumber(boardKind, otherEp.pinName);
|
||||||
if (pin !== null) return { arduinoPin: pin, crossedActiveDevice: activeSeen };
|
// The board id travels with the pin: a QEMU-Linux board has no MCU
|
||||||
|
// simulator, so an input part needs to know WHICH board's bridge to
|
||||||
|
// push the level into (see the pi-aware simulator below).
|
||||||
|
if (pin !== null)
|
||||||
|
return {
|
||||||
|
arduinoPin: pin,
|
||||||
|
crossedActiveDevice: activeSeen,
|
||||||
|
boardId: boardEp?.id ?? otherEp.componentId,
|
||||||
|
};
|
||||||
} else {
|
} else {
|
||||||
const comp = state.components.find((c) => c.id === otherEp.componentId);
|
const comp = state.components.find((c) => c.id === otherEp.componentId);
|
||||||
if (!chipNeighbour && comp?.metadataId === 'custom-chip') {
|
if (!chipNeighbour && comp?.metadataId === 'custom-chip') {
|
||||||
|
|
@ -577,7 +590,44 @@ export const DynamicComponent: React.FC<DynamicComponentProps> = ({
|
||||||
// null pin lookup (`getArduinoPin` returns null when there's no board),
|
// null pin lookup (`getArduinoPin` returns null when there's no board),
|
||||||
// so the stub below is enough — it satisfies the type signature without
|
// so the stub below is enough — it satisfies the type signature without
|
||||||
// doing anything when called.
|
// doing anything when called.
|
||||||
|
// A QEMU-Linux board (Raspberry Pi family, UNIHIKER) has no MCU
|
||||||
|
// simulator: the guest IS the CPU. An input part still calls
|
||||||
|
// `simulator.setPinState(pin, level)` to report a button press or a
|
||||||
|
// PIR trip, and that call used to land on the legacy AVR instance and
|
||||||
|
// vanish — clicking the sensor did nothing at all. Route it to the
|
||||||
|
// bridge of the board this component is actually wired to: `gpio_in`
|
||||||
|
// for the guest, the canvas-fed `pin<N>` value the browser engine's
|
||||||
|
// shims read, and the PinManager so wires and SPICE see the edge.
|
||||||
|
const piBoardId = (() => {
|
||||||
|
const st = useSimulatorStore.getState();
|
||||||
|
const ownPins = new Set<string>();
|
||||||
|
for (const w of st.wires) {
|
||||||
|
if (w.start.componentId === id) ownPins.add(w.start.pinName);
|
||||||
|
if (w.end.componentId === id) ownPins.add(w.end.pinName);
|
||||||
|
}
|
||||||
|
for (const pinName of ownPins) {
|
||||||
|
const { boardId } = traceDetailed(st, id, pinName, 0);
|
||||||
|
const board = boardId ? st.boards.find((b) => b.id === boardId) : undefined;
|
||||||
|
if (board && isPiBoardKind(board.boardKind)) return board.id;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
})();
|
||||||
|
const piSimulator = piBoardId
|
||||||
|
? ({
|
||||||
|
setPinState: (pin: number, state: boolean) => {
|
||||||
|
getBoardBridge(piBoardId)?.sendPinEvent(pin, state);
|
||||||
|
getBoardBridge(piBoardId)?.setSensorState({ [`pin${pin}`]: state ? 1 : 0 });
|
||||||
|
getBoardPinManager(piBoardId)?.triggerPinChange(pin, state, 'external');
|
||||||
|
},
|
||||||
|
isRunning: () =>
|
||||||
|
!!useSimulatorStore.getState().boards.find((b) => b.id === piBoardId)?.running,
|
||||||
|
pinManager: getBoardPinManager(piBoardId),
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
} as any)
|
||||||
|
: null;
|
||||||
|
|
||||||
const stubSimulator =
|
const stubSimulator =
|
||||||
|
piSimulator ??
|
||||||
simulator ??
|
simulator ??
|
||||||
({
|
({
|
||||||
setPinState: () => {},
|
setPinState: () => {},
|
||||||
|
|
|
||||||
|
|
@ -153,6 +153,28 @@ export function getProBoard(kind: string): ProBoardDef | undefined {
|
||||||
return registry.get(kind);
|
return registry.get(kind);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ── Guest setup lines for boards the OSS tree already owns ───────────────
|
||||||
|
//
|
||||||
|
// A QEMU-Linux board that is NOT overlay-registered (the Raspberry Pi
|
||||||
|
// family) may still need a line run at the boot prompt — an overlay can
|
||||||
|
// mount something on the guest for it. Registering a whole ProBoardDef
|
||||||
|
// just to carry that string is the wrong tool: `getProBoard` is what
|
||||||
|
// BoardOnCanvas checks to decide whether to render the overlay's custom
|
||||||
|
// element instead of the OSS board art, so a stub def silently replaced
|
||||||
|
// the Pi's illustration with a bare schematic box and left the wires
|
||||||
|
// hanging at the corner. This registry carries the line and nothing else.
|
||||||
|
const guestSetups = new Map<string, string>();
|
||||||
|
|
||||||
|
export function registerGuestSetup(kind: string, line: string): void {
|
||||||
|
guestSetups.set(kind, line);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** The line to run at the guest's boot prompt: an overlay board's own
|
||||||
|
* `guestSetup` first, then anything registered for an OSS board kind. */
|
||||||
|
export function getGuestSetup(kind: string): string | undefined {
|
||||||
|
return registry.get(kind)?.guestSetup ?? guestSetups.get(kind);
|
||||||
|
}
|
||||||
|
|
||||||
export function listProBoards(): ProBoardDef[] {
|
export function listProBoards(): ProBoardDef[] {
|
||||||
return Array.from(registry.values());
|
return Array.from(registry.values());
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,11 @@
|
||||||
import { create } from 'zustand';
|
import { create } from 'zustand';
|
||||||
import { decideEngine, getInstantEngine } from '../lib/instantEngine';
|
import { decideEngine, getInstantEngine } from '../lib/instantEngine';
|
||||||
import { getProBoard, isProBoardSimulator, type ProBoardSimulator } from '../lib/proBoardRegistry';
|
import {
|
||||||
|
getProBoard,
|
||||||
|
getGuestSetup,
|
||||||
|
isProBoardSimulator,
|
||||||
|
type ProBoardSimulator,
|
||||||
|
} from '../lib/proBoardRegistry';
|
||||||
import { AVRSimulator } from '../simulation/AVRSimulator';
|
import { AVRSimulator } from '../simulation/AVRSimulator';
|
||||||
import { RP2040Simulator } from '../simulation/RP2040Simulator';
|
import { RP2040Simulator } from '../simulation/RP2040Simulator';
|
||||||
import { RiscVSimulator } from '../simulation/RiscVSimulator';
|
import { RiscVSimulator } from '../simulation/RiscVSimulator';
|
||||||
|
|
@ -1291,8 +1296,7 @@ export const useSimulatorStore = create<SimulatorState>((set, get) => {
|
||||||
// it runs before piBooted flips so the VFS upload (gated on piBooted)
|
// it runs before piBooted flips so the VFS upload (gated on piBooted)
|
||||||
// cannot interleave with it.
|
// cannot interleave with it.
|
||||||
bridge.onBooted = () => {
|
bridge.onBooted = () => {
|
||||||
const proDef = getProBoard(boardKind);
|
const setup = getGuestSetup(boardKind);
|
||||||
const setup = proDef?.guestSetup;
|
|
||||||
const flip = () =>
|
const flip = () =>
|
||||||
set((s) => ({
|
set((s) => ({
|
||||||
boards: s.boards.map((b) => (b.id === id ? { ...b, piBooted: true } : b)),
|
boards: s.boards.map((b) => (b.id === id ? { ...b, piBooted: true } : b)),
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue