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

90 lines
3.2 KiB
TypeScript
Raw Normal View History

/**
* start.ts — production wiring for the mixed-mode simulator.
*
* The single function `startSimulation()` mounts everything the
* editor needs to run circuits end-to-end against the WASM ngspice:
*
* 1. CircuitSimulationService — orchestrates solves, publishes to
* useElectricalStore.
* 2. connectAnalogInputsToMcu — subscribes to useElectricalStore
* and pushes voltages into MCU ADCs.
* 3. connectMcuEdgesToService — MCU pin transitions trigger
* scheduler.alterSource + republish via the service.
*
* Replaces the old EditorPage useEffect that chained the legacy
* wireElectricalSolver + connectLegacySolverToMixedMode +
* connectMixedModeSchedulerToStore + connectAnalogInputsToMcu.
*
* Phase 1c step G1 of the mixed-mode migration.
*/
import { useSimulatorStore } from '../../store/useSimulatorStore';
import { useElectricalStore } from '../../store/useElectricalStore';
import {
getMixedModeScheduler,
} from './MixedModeScheduler';
import {
CircuitSimulationService,
type SimulatorStorePort,
type ElectricalStorePort,
type MixedModeSchedulerPort,
type ElectricalSnapshot,
} from './CircuitSimulationService';
import { connectAnalogInputsToMcu } from './connectAnalogInputsToMcu';
import { connectMcuEdgesToService } from './connectMcuEdgesToService';
feat(sim): Phase 1c G+F3 — retire legacy CircuitScheduler / eecircuit-engine The mixed-mode migration's endgame. After this commit there is ONE SPICE solver path in the codebase — the vendored ngspice WASM via SolverPort, behind both NgSpiceWorkerAdapter (production browser) and NgSpiceNodeAdapter (Vitest Node). Zero hybrids; zero legacy left to maintain. Deleted production files: • simulation/spice/CircuitScheduler.ts (200ms-poll legacy) • simulation/spice/SpiceEngine.ts (eecircuit-engine wrap) • simulation/spice/SpiceEngine.lazy.ts (lazy code-split) • simulation/spice/subscribeToStore.ts (legacy solve loop) • simulation/spice/connectLegacySolverToMixedMode.ts (bridge) • simulation/spice/connectMixedModeSchedulerToStore.ts (feature flag) Deleted tests (no longer cover any live code): • connect-legacy-solver-to-mixed-mode.test.ts • connect-mixed-mode-scheduler-to-store.test.ts • spice-rectifier-live-bootstrap.test.ts Migrated 6 tests off the deleted `circuitScheduler.solveNow` API to the new `__tests__/helpers/solveInput.ts` (same shape, backed by NgSpiceNodeAdapter). `useElectricalStore` rewritten as a pure state container: • setSolveResult(snapshot) — atomic publish from the service • paused / setPaused — UI control unchanged • reset — project unload • REMOVED: triggerSolve, solveNow, setDebounceMs, scheduler hook • REMOVED: dependency on SpiceEngine.lazy preload EditorPage now mounts a single `startSimulation()` from `simulation/spice/start.ts`, which constructs CircuitSimulationService + ADC bridge + MCU edge bridge. Four useEffect calls collapsed to one. `circuitVerifier.ts` (production) and `runNetlist.ts` use an environment-aware factory: Web Worker in browser, in-proc WASM in Node tests. `/* @vite-ignore */` keeps the Node adapter chain (node:fs, node:url) out of the browser bundle while still letting Node resolve it dynamically. Removed `eecircuit-engine` from package.json dependencies. `collectPinStates` extracted to its own module so the service doesn't depend on the (now deleted) subscribeToStore.ts. Verification: • 1392/1392 tests pass across 103 files (28 pre-existing skips). • `tsc --noEmit` clean. • `vite build` succeeds (27 s, only the existing chunk-size warning that pre-dates this work). Phase 1c — COMPLETE. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-16 02:46:34 +07:00
import { collectPinStates } from './collectPinStates';
/** Adapt useElectricalStore to the ElectricalStorePort. */
function createElectricalStorePort(): ElectricalStorePort {
return {
publish(snapshot: ElectricalSnapshot): void {
feat(sim): Phase 1c G+F3 — retire legacy CircuitScheduler / eecircuit-engine The mixed-mode migration's endgame. After this commit there is ONE SPICE solver path in the codebase — the vendored ngspice WASM via SolverPort, behind both NgSpiceWorkerAdapter (production browser) and NgSpiceNodeAdapter (Vitest Node). Zero hybrids; zero legacy left to maintain. Deleted production files: • simulation/spice/CircuitScheduler.ts (200ms-poll legacy) • simulation/spice/SpiceEngine.ts (eecircuit-engine wrap) • simulation/spice/SpiceEngine.lazy.ts (lazy code-split) • simulation/spice/subscribeToStore.ts (legacy solve loop) • simulation/spice/connectLegacySolverToMixedMode.ts (bridge) • simulation/spice/connectMixedModeSchedulerToStore.ts (feature flag) Deleted tests (no longer cover any live code): • connect-legacy-solver-to-mixed-mode.test.ts • connect-mixed-mode-scheduler-to-store.test.ts • spice-rectifier-live-bootstrap.test.ts Migrated 6 tests off the deleted `circuitScheduler.solveNow` API to the new `__tests__/helpers/solveInput.ts` (same shape, backed by NgSpiceNodeAdapter). `useElectricalStore` rewritten as a pure state container: • setSolveResult(snapshot) — atomic publish from the service • paused / setPaused — UI control unchanged • reset — project unload • REMOVED: triggerSolve, solveNow, setDebounceMs, scheduler hook • REMOVED: dependency on SpiceEngine.lazy preload EditorPage now mounts a single `startSimulation()` from `simulation/spice/start.ts`, which constructs CircuitSimulationService + ADC bridge + MCU edge bridge. Four useEffect calls collapsed to one. `circuitVerifier.ts` (production) and `runNetlist.ts` use an environment-aware factory: Web Worker in browser, in-proc WASM in Node tests. `/* @vite-ignore */` keeps the Node adapter chain (node:fs, node:url) out of the browser bundle while still letting Node resolve it dynamically. Removed `eecircuit-engine` from package.json dependencies. `collectPinStates` extracted to its own module so the service doesn't depend on the (now deleted) subscribeToStore.ts. Verification: • 1392/1392 tests pass across 103 files (28 pre-existing skips). • `tsc --noEmit` clean. • `vite build` succeeds (27 s, only the existing chunk-size warning that pre-dates this work). Phase 1c — COMPLETE. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-16 02:46:34 +07:00
useElectricalStore.getState().setSolveResult({
nodeVoltages: snapshot.nodeVoltages,
branchCurrents: snapshot.branchCurrents,
pinNetMap: snapshot.pinNetMap,
analysisMode: snapshot.analysisMode,
timeWaveforms: snapshot.timeWaveforms,
feat(sim): Phase 1c G+F3 — retire legacy CircuitScheduler / eecircuit-engine The mixed-mode migration's endgame. After this commit there is ONE SPICE solver path in the codebase — the vendored ngspice WASM via SolverPort, behind both NgSpiceWorkerAdapter (production browser) and NgSpiceNodeAdapter (Vitest Node). Zero hybrids; zero legacy left to maintain. Deleted production files: • simulation/spice/CircuitScheduler.ts (200ms-poll legacy) • simulation/spice/SpiceEngine.ts (eecircuit-engine wrap) • simulation/spice/SpiceEngine.lazy.ts (lazy code-split) • simulation/spice/subscribeToStore.ts (legacy solve loop) • simulation/spice/connectLegacySolverToMixedMode.ts (bridge) • simulation/spice/connectMixedModeSchedulerToStore.ts (feature flag) Deleted tests (no longer cover any live code): • connect-legacy-solver-to-mixed-mode.test.ts • connect-mixed-mode-scheduler-to-store.test.ts • spice-rectifier-live-bootstrap.test.ts Migrated 6 tests off the deleted `circuitScheduler.solveNow` API to the new `__tests__/helpers/solveInput.ts` (same shape, backed by NgSpiceNodeAdapter). `useElectricalStore` rewritten as a pure state container: • setSolveResult(snapshot) — atomic publish from the service • paused / setPaused — UI control unchanged • reset — project unload • REMOVED: triggerSolve, solveNow, setDebounceMs, scheduler hook • REMOVED: dependency on SpiceEngine.lazy preload EditorPage now mounts a single `startSimulation()` from `simulation/spice/start.ts`, which constructs CircuitSimulationService + ADC bridge + MCU edge bridge. Four useEffect calls collapsed to one. `circuitVerifier.ts` (production) and `runNetlist.ts` use an environment-aware factory: Web Worker in browser, in-proc WASM in Node tests. `/* @vite-ignore */` keeps the Node adapter chain (node:fs, node:url) out of the browser bundle while still letting Node resolve it dynamically. Removed `eecircuit-engine` from package.json dependencies. `collectPinStates` extracted to its own module so the service doesn't depend on the (now deleted) subscribeToStore.ts. Verification: • 1392/1392 tests pass across 103 files (28 pre-existing skips). • `tsc --noEmit` clean. • `vite build` succeeds (27 s, only the existing chunk-size warning that pre-dates this work). Phase 1c — COMPLETE. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-16 02:46:34 +07:00
converged: snapshot.warnings.length === 0,
error: snapshot.warnings[0] ?? null,
lastSolveMs: 0,
submittedNetlist: '',
});
},
};
}
/**
* Mount the simulation loop. Returns an unsubscribe handle for
* editor cleanup.
*/
export function startSimulation(): () => void {
const service = new CircuitSimulationService(
useSimulatorStore as unknown as SimulatorStorePort,
createElectricalStorePort(),
getMixedModeScheduler() as unknown as MixedModeSchedulerPort,
{
collectBoardPinStates: (boardId, boardKind, wires) =>
collectPinStates(
boardId,
boardKind as Parameters<typeof collectPinStates>[1],
wires as Parameters<typeof collectPinStates>[2],
),
},
);
// Phase 1d #3 — pre-boot the WASM engine the moment the editor
// mounts. Without this, the first solve (typically the user's
// first canvas edit) pays the full WASM init cost (~2-5 s) and the
// canvas appears frozen. By kicking init now, the Worker boots
// while the user looks at the empty canvas; by the time they wire
// anything, the engine is warm.
void getMixedModeScheduler().start();
const unsubService = service.start();
const unsubAdc = connectAnalogInputsToMcu();
const unsubEdges = connectMcuEdgesToService(service);
return () => {
unsubService();
unsubAdc();
unsubEdges();
};
}