velxio/frontend/src/simulation/spice/storeAdapter.ts

63 lines
1.8 KiB
TypeScript
Raw Normal View History

feat: electrical simulation via ngspice-WASM (eecircuit-engine) Adds full SPICE-accurate electrical simulation to Velxio, behind a lazy- loaded ⚡ toolbar toggle. Arduino / ESP32 / RP2040 sketches now co-simulate with real analog behaviour: correct voltages on wires, real I–V curves on LEDs, working potentiometers, NTC thermistors read by analogRead(), PWM driving RC filters, transistors, op-amps, diodes, MOSFETs, etc. Engine: eecircuit-engine (ngspice compiled to WebAssembly). Main bundle stays at 2.4 MB; the 20 MB SPICE chunk only loads when the user activates electrical mode. Disabled at build time via VITE_ELECTRICAL_SIM=false. Frontend additions: - simulation/spice/: SpiceEngine wrapper + lazy entry, NetlistBuilder with UnionFind over wires, componentToSpice mapping (24 metadataIds incl. real part numbers: 2N2222, 2N3055, BC547, IRF540, 2N7000, 1N4148, 1N4007, 1N4733, LEDs, NTC, op-amp ideal), CircuitScheduler with debounced coalescing, AVRSpiceBridge for quasi-static co-simulation. - store/useElectricalStore: Zustand slice, feature-flag aware. - components/analog-ui/: ⚡ toolbar toggle + SVG voltage overlay. - components/components-instruments/: Voltmeter, Ammeter probes. - 62 tests (spice-*, netlist-builder, component-to-spice, instruments). Sandbox (test/test_circuit/): 47-test validation sandbox that proved the approach (hand-rolled MNA baseline + ngspice pipeline) before porting to the app. Kept as reference. Docs: docs/wiki/circuit-emulation-*.md (13 engineering pages covering architecture, solvers, components, AVR bridge, gotchas, performance, integration plan, API reference, appendix) + electrical-simulation- user-guide.md (end-user facing). Reference plan: test/test_circuit/plan/phase_8_velxio_implementation.md Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 19:11:54 +07:00
/**
* Bridge between Velxio's simulator store (components, wires, boards) and
* the NetlistBuilder inputs. Kept separate so the SPICE engine never has
* to import the full Zustand store or its types.
*
* Callers construct a `BuildNetlistInput` by calling
* `buildInputFromStore(storeSnapshot)`
*/
import type { BuildNetlistInput, BoardForSpice, ComponentForSpice, WireForSpice, PinSourceState } from './types';
import type { Wire } from '../../types/wire';
import type { BoardKind } from '../../types/board';
import { BOARD_PIN_GROUPS } from './boardPinGroups';
export interface StoreSnapshot {
components: Array<{
id: string;
metadataId: string;
properties: Record<string, unknown>;
}>;
wires: Wire[];
boards: Array<{
id: string;
boardKind: BoardKind;
pinStates: Record<string, PinSourceState>; // caller pre-populates from PinManager + PWM
}>;
}
/**
* Convert a Velxio store snapshot into the `BuildNetlistInput` consumed
* by the NetlistBuilder.
*/
export function buildInputFromStore(snap: StoreSnapshot): BuildNetlistInput {
const components: ComponentForSpice[] = snap.components.map((c) => ({
id: c.id,
metadataId: c.metadataId,
properties: c.properties,
}));
const wires: WireForSpice[] = snap.wires.map((w) => ({
id: w.id,
start: { componentId: w.start.componentId, pinName: w.start.pinName },
end: { componentId: w.end.componentId, pinName: w.end.pinName },
}));
const boards: BoardForSpice[] = snap.boards.map((b) => {
const group = BOARD_PIN_GROUPS[b.boardKind] ?? BOARD_PIN_GROUPS.default;
return {
id: b.id,
vcc: group.vcc,
pins: b.pinStates,
groundPinNames: group.gnd,
vccPinNames: group.vcc_pins,
};
});
return {
components,
wires,
boards,
analysis: { kind: 'op' },
};
}