@mica-js/simulator 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -56,20 +56,20 @@ const report = executor.advance(timePs(100_000)); // 100 ns,同步完成
56
56
 
57
57
  ## 接口
58
58
 
59
- | 成员 | 说明 |
60
- | -------------------------- | ------------------------------------------------------------------- |
61
- | `getState()` | `'idle'`、`'ready'`、`'faulted'`、`'disposed'`;只有 `ready` 能推进 |
62
- | `getSimTime()` | 当前虚拟时间;首次加载前为 `0` |
63
- | `getSchematic()` | 当前运行时的来源文档;首次加载前是空图 |
64
- | `getTopics()` | 每个实例的端口,`symbol` + `port` 与画布绑定的端点一一对应 |
65
- | `loadSchematic(document)` | 按符号建实例并跑 `initialize`,时钟归零;构建失败时旧运行时原样保留 |
66
- | `advance(delta)` | 同步推进 `delta` 皮秒,返回 `{ from, to, executed, reason }` |
67
- | `onPublish(listener)` | 器件发布 `(target, payload, simTime)`;返回取消订阅函数 |
68
- | `deliver(target, payload)` | 排入下一次 `advance` 的开头,同一时刻多次调用按调用顺序被器件看到 |
69
- | `reset()` | 丢弃实例、按当前原理图重建,时钟归零;同步 |
70
- | `dispose()` | 销毁全部实例并释放运行时;同步、幂等 |
71
-
72
- `DeviceTopic` 的 `direction` / `type` 与 `DevicePortDescriptor` 同构,宿主可以直接用 `getTopics()` 生成画布的绑定候选。
59
+ | 成员 | 说明 |
60
+ | -------------------------- | --------------------------------------------------------------------------------------------- |
61
+ | `getState()` | `'idle'`、`'ready'`、`'faulted'`、`'disposed'`;只有 `ready` 能推进 |
62
+ | `getSimTime()` | 当前虚拟时间;首次加载前为 `0` |
63
+ | `getSchematic()` | 当前运行时的来源文档;首次加载前是空图 |
64
+ | `getTopics()` | 每个实例的端口,`symbol`(id,绑定用的就是它)+ `designator`(位号,未设置时等于 id)+ `port` |
65
+ | `loadSchematic(document)` | 按符号建实例并跑 `initialize`,时钟归零;构建失败时旧运行时原样保留 |
66
+ | `advance(delta)` | 同步推进 `delta` 皮秒,返回 `{ from, to, executed, reason }` |
67
+ | `onPublish(listener)` | 器件发布 `(target, payload, simTime)`;返回取消订阅函数 |
68
+ | `deliver(target, payload)` | 排入下一次 `advance` 的开头,同一时刻多次调用按调用顺序被器件看到 |
69
+ | `reset()` | 丢弃实例、按当前原理图重建,时钟归零;同步 |
70
+ | `dispose()` | 销毁全部实例并释放运行时;同步、幂等 |
71
+
72
+ `DeviceTopic` 的 `direction` / `type` 与 `DevicePortDescriptor` 同构,宿主可以直接用 `getTopics()` 生成画布的绑定候选。`symbol` 是原理图里的符号 id(绑定存的就是它),`designator` 是给人看的位号(`designatorOf`:符号自己的 `designator`,没写时回退到 id),两者在界面上不要混用。
73
73
 
74
74
  ## 语义
75
75
 
@@ -82,7 +82,27 @@ const report = executor.advance(timePs(100_000)); // 100 ns,同步完成
82
82
 
83
83
  诊断码以 `simulator/` 开头:`device-load-failed`、`instance-create-failed`、`advance-failed`、`publish-failed`、`dispose-failed`。宿主调用的参数错误抛 `SimulatorError`(`code` 见 `SimulatorErrorCode`)。
84
84
 
85
+ ## 电气层
86
+
87
+ 原理图里的导线就是电路:`createCircuitHost()` 返回一个宿主接线工厂,用 `deriveSchematicTopology` 把每个器件引脚按合成符号的几何位置落到网,折成 `TopologyDefinition` 交给 `createPowerRuntime` 解算,再把结果作为 `pinHost` / `topology` 交给执行器。执行器自身不解析电路,这条路径是可选的一层便利封装。
88
+
89
+ ```ts
90
+ const executor = createDeviceSimExecutor({
91
+ devices: provider,
92
+ host: createCircuitHost(), // 5 V 供电;createCircuitHost({ supplyVoltage: 3.3 }) 可改
93
+ });
94
+ ```
95
+
96
+ - **网 → 外部导线。** 每张网一条 `wireId`,成员是网里的全部引脚;带地符号的网标 `globalGnd`。
97
+ - **电源符号 → 电源实例。** `builtin:power` / `builtin:ground` 属于合成实例 `power`:供电网的引脚按 `supplyVoltage`(默认 5 V)模拟输出,接地网的引脚按逻辑低电压强驱动。地是 0 V 参考,不是悬空——所以「触点接地」真的能把引脚拉低。
98
+ - **器件引脚 → 拓扑引脚。** 引脚种类取自器件描述(`signal` / `power` / `gnd`);器件描述里的 `connections` 变成可开关的内部连接,`connection.apply` 直接作用在这张网上。引脚几何来自 `DeviceSimHostContext.modules`,所以工厂不会重复加载器件。
99
+ - **引脚初值一条不少。** `PowerOptions.pinStates` 要求每个已声明引脚一条,默认高阻、无上下拉;器件在 `initialize` 里 `pin.configure` 之后由器件层覆盖。
100
+ - **生命周期跟着运行时走。** 实例先销毁(退订、停观察者),工厂的 `dispose` 最后销毁电气网络;构建失败时同样按这个顺序回收。
101
+
102
+ 按钮 + 计数器 + 数码管的完整样例见 `apps/playground/public/devices/`,`apps/playground/scripts/build-device-demo.mjs` 负责在改动布局后重算导线端点。
103
+
85
104
  ## 边界
86
105
 
87
- - **电路定义归宿主所有。** 执行器只透传 `pinHost` / `topology`,不解析板级电源、上拉或网络拓扑;引脚与电气观测(`sampleDigital`、`connectionState`)由宿主读自己的运行时。
106
+ - **电路定义归宿主所有。** 执行器只透传 `pinHost` / `topology`,不解析板级电源、上拉或网络拓扑;引脚与电气观测(`sampleDigital`、`connectionState`)由宿主读自己的运行时。`createCircuitHost` 是可选的实现之一,手写电路(例如 playground 的仿真调试页)可以直接实现 `DeviceSimHostFactory`。
107
+ - **模拟量只有理想电压源。** `createCircuitHost` 的引脚模型固定为 `ideal-voltage`(0–5 V,低 0.8 V、高 2 V),没有电流、RC 或器件间负载;需要更细的电气行为时要往 `@mica-js/core` 的电气层加模型,或换一个自定义宿主。
88
108
  - 器件代码在宿主 realm 里求值(与 `loadDeviceModule` 相同,等同可信扩展),沙箱化未做。
package/dist/index.d.ts CHANGED
@@ -1,6 +1,9 @@
1
1
  import { DeviceInstanceOptions } from '@mica-js/device';
2
2
  import { DevicePinHost } from '@mica-js/device';
3
+ import { ElectricalPinInput } from '@mica-js/core';
3
4
  import { IDeviceProvider } from '@mica-js/device';
5
+ import { LoadedDeviceModule } from '@mica-js/device';
6
+ import { PowerDefinition } from '@mica-js/core';
4
7
  import { Scheduler } from '@mica-js/core';
5
8
  import { SchedulerOptions } from '@mica-js/core';
6
9
  import { SchematicDocument } from '@mica-js/core/schematic';
@@ -8,6 +11,7 @@ import { SchematicSymbol } from '@mica-js/core/schematic';
8
11
  import { TimePs } from '@mica-js/core';
9
12
  import { TopicPayload } from '@mica-js/device';
10
13
  import { TopicPayloadType } from '@mica-js/device';
14
+ import { TopologyDefinition } from '@mica-js/core';
11
15
  import { TopologyRuntime } from '@mica-js/core';
12
16
 
13
17
  export declare type AdvanceReason = 'until' | 'empty' | 'entry-limit' | 'paused';
@@ -22,12 +26,37 @@ export declare interface AdvanceReport {
22
26
  readonly reason: AdvanceReason;
23
27
  }
24
28
 
29
+ /** Everything `createPowerRuntime` and the device instances need for one schematic. */
30
+ export declare interface CircuitLayout {
31
+ readonly topology: TopologyDefinition;
32
+ readonly power: PowerDefinition;
33
+ /** Complete held-intent snapshot: one entry per declared pin. */
34
+ readonly pinStates: readonly ElectricalPinInput[];
35
+ }
36
+
37
+ export declare interface CircuitOptions {
38
+ /** Voltage the schematic's power symbols hold, in volts. Defaults to 5. */
39
+ readonly supplyVoltage?: number;
40
+ }
41
+
42
+ /**
43
+ * Wiring host for schemes that are drawn, not described.
44
+ *
45
+ * Pass it as `DeviceSimExecutorOptions.host` and the executor mounts one electrical network per
46
+ * runtime: the sheet's wires become nets, power/ground symbols become the supply rails, and every
47
+ * device instance reads real pin voltages and connection states instead of host-recorded values.
48
+ *
49
+ * @example
50
+ * createDeviceSimExecutor({ devices, host: createCircuitHost() });
51
+ */
52
+ export declare function createCircuitHost(options?: CircuitOptions): DeviceSimHostFactory;
53
+
25
54
  /**
26
55
  * Creates an executor for one schematic at a time.
27
56
  *
28
- * Instances are provisioned by `@mica-js/device` on a private scheduler; pins, nets and the
29
- * electrical network are not wired yet, so a device whose IR touches pins or connections fails to
30
- * load with the device layer's own error.
57
+ * Every component symbol becomes a device instance on a private scheduler. Pin and connection
58
+ * wiring comes from `options.host`, which is rebuilt for every runtime so it always belongs to the
59
+ * scheduler that is running; schematic wiring itself is available as `createCircuitHost`.
31
60
  */
32
61
  export declare function createDeviceSimExecutor(options: DeviceSimExecutorOptions): DeviceSimExecutor;
33
62
 
@@ -142,6 +171,13 @@ export declare interface DeviceSimHostContext {
142
171
  /** Scheduler of the runtime being built; a new one for every load and every reset. */
143
172
  readonly scheduler: Scheduler;
144
173
  readonly schematic: SchematicDocument;
174
+ /**
175
+ * Device modules the schematic resolves to, keyed by device id.
176
+ *
177
+ * Pin geometry and connection pairs live in the descriptions, so a wiring host that derives nets
178
+ * from the sheet (see `createCircuitHost`) reads them from here instead of loading them twice.
179
+ */
180
+ readonly modules: ReadonlyMap<string, LoadedDeviceModule>;
145
181
  }
146
182
 
147
183
  /** Builds the wiring for one runtime. */
@@ -162,8 +198,13 @@ export declare type DeviceSimInstanceOptions = Omit<DeviceInstanceOptions, 'inst
162
198
  * `DevicePortDescriptor`, so a host can build binding candidates without touching the devices.
163
199
  */
164
200
  export declare interface DeviceTopic {
165
- /** Symbol id inside the schematic (`SchematicSymbol.id`). */
201
+ /** Symbol id inside the schematic (`SchematicSymbol.id`); this is what a binding stores. */
166
202
  readonly symbol: string;
203
+ /**
204
+ * Reference designator shown to users (`U1`), following `designatorOf`: the symbol's own
205
+ * `designator`, or its id when the schematic does not set one.
206
+ */
207
+ readonly designator: string;
167
208
  /** Port key declared by the device description. */
168
209
  readonly port: string;
169
210
  readonly direction: 'input' | 'output';
package/dist/index.js CHANGED
@@ -1,4 +1,5 @@
1
- import { createScheduler, timePs } from "@mica-js/core";
1
+ import { createPowerRuntime, createScheduler, timePs } from "@mica-js/core";
2
+ import { deriveSchematicTopology, designatorOf, symbolRole, synthesizeDeviceSymbol } from "@mica-js/core/schematic";
2
3
  import { createDeviceInstance, loadDeviceModule } from "@mica-js/device";
3
4
  //#region src/errors.ts
4
5
  /** Error raised by the executor's host-facing calls. */
@@ -13,6 +14,215 @@ var SimulatorError = class extends Error {
13
14
  }
14
15
  };
15
16
  //#endregion
17
+ //#region src/circuit.ts
18
+ /** Synthetic pin owner for the supplies the schematic's power/ground symbols name. */
19
+ var SUPPLY_INSTANCE = "power";
20
+ /** Scheduler identity of the electrical network; distinct from every symbol id. */
21
+ var POWER_INSTANCE_ID = "simulator/power";
22
+ /** Every pin uses the same digital interpretation; module pins override mode and pull. */
23
+ var PIN_MODEL = Object.freeze({
24
+ kind: "ideal-voltage",
25
+ minVoltage: 0,
26
+ maxVoltage: 5,
27
+ lowVoltage: .8,
28
+ highVoltage: 2
29
+ });
30
+ /**
31
+ * Wiring host for schemes that are drawn, not described.
32
+ *
33
+ * Pass it as `DeviceSimExecutorOptions.host` and the executor mounts one electrical network per
34
+ * runtime: the sheet's wires become nets, power/ground symbols become the supply rails, and every
35
+ * device instance reads real pin voltages and connection states instead of host-recorded values.
36
+ *
37
+ * @example
38
+ * createDeviceSimExecutor({ devices, host: createCircuitHost() });
39
+ */
40
+ function createCircuitHost(options = {}) {
41
+ return ({ scheduler, schematic, modules }) => {
42
+ const circuit = buildCircuit(schematic, modules, options);
43
+ if (circuit === void 0) return {};
44
+ const runtime = createPowerRuntime(scheduler, circuit.power, {
45
+ instanceId: POWER_INSTANCE_ID,
46
+ pinStates: circuit.pinStates,
47
+ floating: { seed: 1 }
48
+ });
49
+ return {
50
+ pinHost: runtime,
51
+ topology: runtime,
52
+ dispose: () => {
53
+ runtime.destroy();
54
+ }
55
+ };
56
+ };
57
+ }
58
+ /**
59
+ * Turns a schematic into a topology plus power definition.
60
+ *
61
+ * Wire geometry is resolved by `deriveSchematicTopology` against synthesized symbols, so the
62
+ * simulator never has to know pin coordinates. Every device pin becomes a topology pin, each net
63
+ * becomes one external wire, and the device descriptions' connections become the switchable pairs.
64
+ *
65
+ * @returns `undefined` when the schematic declares no pins at all (nothing to solve).
66
+ */
67
+ function buildCircuit(document, modules, options = {}) {
68
+ const supplyVoltage = options.supplyVoltage ?? 5;
69
+ const derived = deriveSchematicTopology(document, { devices: createSymbolProvider(modules) });
70
+ if (derived.pins.length === 0) return;
71
+ const kinds = pinKinds(document, modules);
72
+ const pins = derived.pins.map((pin) => ({
73
+ instanceId: pin.symbolId,
74
+ pinName: pin.pinId,
75
+ kind: kinds.get(`${pin.symbolId}\u0000${pin.pinId}`) ?? "signal"
76
+ }));
77
+ const wires = [];
78
+ let supplyPin;
79
+ let groundPin;
80
+ for (const [index, net] of derived.nets.entries()) {
81
+ const members = net.members.filter((member) => member.kind === "pin").map((member) => ({
82
+ instanceId: member.symbolId,
83
+ pinName: member.pinId
84
+ }));
85
+ const ground = net.members.some((member) => member.kind === "ground");
86
+ const power = net.members.some((member) => member.kind === "power");
87
+ const name = net.name ?? `net-${index}`;
88
+ if (power) {
89
+ supplyPin ??= name;
90
+ pins.push({
91
+ instanceId: SUPPLY_INSTANCE,
92
+ pinName: name,
93
+ kind: "power"
94
+ });
95
+ members.push({
96
+ instanceId: SUPPLY_INSTANCE,
97
+ pinName: name
98
+ });
99
+ }
100
+ if (ground) {
101
+ groundPin ??= name;
102
+ pins.push({
103
+ instanceId: SUPPLY_INSTANCE,
104
+ pinName: name,
105
+ kind: "gnd"
106
+ });
107
+ members.push({
108
+ instanceId: SUPPLY_INSTANCE,
109
+ pinName: name
110
+ });
111
+ }
112
+ if (members.length > 0) wires.push({
113
+ wireId: `net-${index}`,
114
+ pins: members,
115
+ ...ground ? { globalGnd: true } : {}
116
+ });
117
+ }
118
+ const devices = [];
119
+ if (supplyPin !== void 0 && groundPin !== void 0) devices.push({
120
+ instanceId: SUPPLY_INSTANCE,
121
+ powerDomains: { board: {
122
+ supply: supplyPin,
123
+ ground: groundPin,
124
+ voltage: {
125
+ turnOn: 3,
126
+ turnOff: 2
127
+ }
128
+ } }
129
+ });
130
+ const topology = {
131
+ pins,
132
+ wires,
133
+ connections: deviceConnections(document, modules)
134
+ };
135
+ return {
136
+ topology,
137
+ power: {
138
+ topology,
139
+ devices
140
+ },
141
+ pinStates: pins.map((pin) => {
142
+ const rail = pin.instanceId === SUPPLY_INSTANCE;
143
+ if (rail && pin.kind === "power") return {
144
+ instanceId: pin.instanceId,
145
+ pinName: pin.pinName,
146
+ powerReady: true,
147
+ mode: "analog",
148
+ outputEnabled: true,
149
+ pull: "none",
150
+ voltageIntent: supplyVoltage,
151
+ model: PIN_MODEL
152
+ };
153
+ if (rail) return {
154
+ instanceId: pin.instanceId,
155
+ pinName: pin.pinName,
156
+ powerReady: true,
157
+ mode: "push-pull",
158
+ outputEnabled: true,
159
+ pull: "none",
160
+ digitalIntent: false,
161
+ model: PIN_MODEL
162
+ };
163
+ return {
164
+ instanceId: pin.instanceId,
165
+ pinName: pin.pinName,
166
+ powerReady: true,
167
+ mode: "high-z",
168
+ outputEnabled: false,
169
+ pull: "none",
170
+ model: PIN_MODEL
171
+ };
172
+ })
173
+ };
174
+ }
175
+ /**
176
+ * Symbols for every device the schematic names.
177
+ *
178
+ * The synthesized geometry must be the same one the `.sch` was authored against, so the name is
179
+ * the full device id and pins keep their declared side; that is what the playground generator uses.
180
+ */
181
+ function createSymbolProvider(modules) {
182
+ const cache = /* @__PURE__ */ new Map();
183
+ const symbolOf = (deviceId) => {
184
+ if (!cache.has(deviceId)) {
185
+ const module = modules.get(deviceId);
186
+ cache.set(deviceId, module === void 0 ? void 0 : synthesizeDeviceSymbol({
187
+ name: deviceId,
188
+ pins: Object.fromEntries([...module.description.pins.values()].map((pin) => [pin.key, { side: pin.side }]))
189
+ }));
190
+ }
191
+ return cache.get(deviceId);
192
+ };
193
+ return {
194
+ getDeviceSymbol: (reference) => symbolOf(`${reference.packageId}:${reference.deviceId}`),
195
+ getDeviceName: (reference) => `${reference.packageId}:${reference.deviceId}`
196
+ };
197
+ }
198
+ function pinKinds(document, modules) {
199
+ const kinds = /* @__PURE__ */ new Map();
200
+ for (const symbol of document.sheet.symbols) {
201
+ const module = modules.get(symbol.device);
202
+ if (module === void 0) continue;
203
+ for (const pin of module.description.pins.values()) kinds.set(`${symbol.id}\u0000${pin.key}`, pin.kind);
204
+ }
205
+ return kinds;
206
+ }
207
+ function deviceConnections(document, modules) {
208
+ const connections = [];
209
+ for (const symbol of document.sheet.symbols) {
210
+ const module = modules.get(symbol.device);
211
+ if (module === void 0) continue;
212
+ for (const [connectionId, descriptor] of module.description.connections) {
213
+ const [first, second] = descriptor.pins;
214
+ if (first === void 0 || second === void 0) continue;
215
+ connections.push({
216
+ instanceId: symbol.id,
217
+ connectionId,
218
+ pins: [first, second],
219
+ initial: descriptor.initial
220
+ });
221
+ }
222
+ }
223
+ return connections;
224
+ }
225
+ //#endregion
16
226
  //#region src/create-device-sim-executor.ts
17
227
  var EMPTY_SCHEMATIC = Object.freeze({
18
228
  version: 1,
@@ -26,9 +236,9 @@ var EMPTY_TOPICS = Object.freeze([]);
26
236
  /**
27
237
  * Creates an executor for one schematic at a time.
28
238
  *
29
- * Instances are provisioned by `@mica-js/device` on a private scheduler; pins, nets and the
30
- * electrical network are not wired yet, so a device whose IR touches pins or connections fails to
31
- * load with the device layer's own error.
239
+ * Every component symbol becomes a device instance on a private scheduler. Pin and connection
240
+ * wiring comes from `options.host`, which is rebuilt for every runtime so it always belongs to the
241
+ * scheduler that is running; schematic wiring itself is available as `createCircuitHost`.
32
242
  */
33
243
  function createDeviceSimExecutor(options) {
34
244
  return new DeviceSimExecutorImpl(options);
@@ -129,7 +339,7 @@ var DeviceSimExecutorImpl = class {
129
339
  /** Loads the devices the schematic names; already loaded ids are reused. */
130
340
  async #loadModules(document) {
131
341
  for (const symbol of document.sheet.symbols) {
132
- if (this.#modules.has(symbol.device)) continue;
342
+ if (!isComponent(symbol) || this.#modules.has(symbol.device)) continue;
133
343
  try {
134
344
  this.#modules.set(symbol.device, await loadDeviceModule(symbol.device, this.#options.devices));
135
345
  } catch (cause) {
@@ -145,12 +355,14 @@ var DeviceSimExecutorImpl = class {
145
355
  const scheduler = this.#options.scheduler === void 0 ? createScheduler() : createScheduler(this.#options.scheduler);
146
356
  const host = this.#options.host?.({
147
357
  scheduler,
148
- schematic: document
358
+ schematic: document,
359
+ modules: this.#modules
149
360
  });
150
361
  const instances = /* @__PURE__ */ new Map();
151
362
  const topics = [];
152
363
  try {
153
364
  for (const symbol of document.sheet.symbols) {
365
+ if (!isComponent(symbol)) continue;
154
366
  const instance = this.#createInstance(symbol, scheduler, host);
155
367
  instances.set(symbol.id, instance);
156
368
  collectTopics(symbol, instance, topics);
@@ -244,9 +456,15 @@ var DeviceSimExecutorImpl = class {
244
456
  } catch {}
245
457
  }
246
458
  };
459
+ /** True when the symbol stands for a device the executor instantiates. */
460
+ function isComponent(symbol) {
461
+ return symbolRole(symbol.device) === "component";
462
+ }
247
463
  function collectTopics(symbol, instance, into) {
464
+ const designator = designatorOf(symbol) ?? symbol.id;
248
465
  for (const [port, descriptor] of instance.description.ports) into.push(Object.freeze({
249
466
  symbol: symbol.id,
467
+ designator,
250
468
  port,
251
469
  direction: descriptor.direction,
252
470
  type: descriptor.type,
@@ -277,6 +495,6 @@ function disposeHostQuietly(host, report) {
277
495
  }
278
496
  }
279
497
  //#endregion
280
- export { SimulatorError, createDeviceSimExecutor };
498
+ export { SimulatorError, createCircuitHost, createDeviceSimExecutor };
281
499
 
282
500
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../src/errors.ts","../src/create-device-sim-executor.ts"],"sourcesContent":["export type SimulatorErrorCode =\n | 'simulator/disposed'\n | 'simulator/not-ready'\n | 'simulator/invalid-delta'\n | 'simulator/unknown-target'\n | 'simulator/unknown-port'\n | 'simulator/device-load-failed'\n | 'simulator/instance-create-failed';\n\nexport interface SimulatorErrorOptions {\n /** Symbol the failure belongs to, when it is attributable to one. */\n readonly symbol?: string;\n readonly cause?: unknown;\n}\n\n/** Error raised by the executor's host-facing calls. */\nexport class SimulatorError extends Error {\n override readonly name = 'SimulatorError';\n readonly code: SimulatorErrorCode;\n readonly symbol: string | undefined;\n\n constructor(code: SimulatorErrorCode, message: string, options: SimulatorErrorOptions = {}) {\n super(message, options.cause === undefined ? undefined : { cause: options.cause });\n this.code = code;\n this.symbol = options.symbol;\n }\n}\n","import { createScheduler, timePs, type Scheduler, type TimePs } from '@mica-js/core';\nimport type { SchematicDocument, SchematicSymbol } from '@mica-js/core/schematic';\nimport {\n createDeviceInstance,\n loadDeviceModule,\n type DeviceInstance,\n type LoadedDeviceModule,\n type TopicPayload,\n} from '@mica-js/device';\nimport { SimulatorError } from './errors';\nimport type {\n AdvanceReport,\n DeviceSimExecutor,\n DeviceSimExecutorOptions,\n DeviceSimHost,\n DevicePublish,\n DeviceTopic,\n DeviceTopicTarget,\n SimulationDiagnostic,\n SimulationState,\n} from './types';\n\nconst EMPTY_SCHEMATIC: SchematicDocument = Object.freeze({\n version: 1,\n sheet: Object.freeze({\n symbols: Object.freeze([]),\n wires: Object.freeze([]),\n junctions: Object.freeze([]),\n }),\n});\n\nconst EMPTY_TOPICS: readonly DeviceTopic[] = Object.freeze([]);\n\n/** One loaded schematic: its scheduler, the instances keyed by symbol id and the topic index. */\ninterface Runtime {\n readonly scheduler: Scheduler;\n readonly instances: ReadonlyMap<string, DeviceInstance>;\n readonly topics: readonly DeviceTopic[];\n /** Wiring the instances were mounted against; released with the runtime. */\n readonly host: DeviceSimHost | undefined;\n}\n\ninterface PendingDelivery {\n readonly target: DeviceTopicTarget;\n readonly payload: unknown;\n}\n\n/**\n * Creates an executor for one schematic at a time.\n *\n * Instances are provisioned by `@mica-js/device` on a private scheduler; pins, nets and the\n * electrical network are not wired yet, so a device whose IR touches pins or connections fails to\n * load with the device layer's own error.\n */\nexport function createDeviceSimExecutor(options: DeviceSimExecutorOptions): DeviceSimExecutor {\n return new DeviceSimExecutorImpl(options);\n}\n\nclass DeviceSimExecutorImpl implements DeviceSimExecutor {\n readonly #options: DeviceSimExecutorOptions;\n readonly #publishListeners = new Set<(publish: DevicePublish) => void>();\n readonly #pending: PendingDelivery[] = [];\n /** Loaded device modules, so `reset` can rebuild instances without touching the provider. */\n readonly #modules = new Map<string, LoadedDeviceModule>();\n #runtime: Runtime | undefined;\n #schematic: SchematicDocument = EMPTY_SCHEMATIC;\n #state: SimulationState = 'idle';\n\n constructor(options: DeviceSimExecutorOptions) {\n this.#options = options;\n }\n\n getState(): SimulationState {\n return this.#state;\n }\n\n getSimTime(): TimePs {\n return this.#runtime?.scheduler.now ?? timePs(0);\n }\n\n getSchematic(): SchematicDocument {\n return this.#schematic;\n }\n\n getTopics(): readonly DeviceTopic[] {\n return this.#runtime?.topics ?? EMPTY_TOPICS;\n }\n\n async loadSchematic(document: SchematicDocument): Promise<void> {\n this.#assertNotDisposed();\n await this.#loadModules(document);\n // Build first: a rejected load must leave the running simulation untouched.\n const runtime = this.#buildRuntime(document);\n this.#teardown();\n this.#runtime = runtime;\n this.#schematic = document;\n this.#state = 'ready';\n }\n\n advance(delta: TimePs): AdvanceReport {\n const runtime = this.#requireRuntime();\n if (!Number.isSafeInteger(delta) || delta <= 0) {\n throw new SimulatorError(\n 'simulator/invalid-delta',\n `advance expects a positive whole number of picoseconds, received ${String(delta)}.`,\n );\n }\n const { scheduler } = runtime;\n const from = scheduler.now;\n this.#flush(runtime);\n try {\n const result = scheduler.start({ until: from + delta });\n return Object.freeze({\n from,\n to: result.timePs,\n executed: result.executed,\n reason: result.reason,\n });\n } catch (cause) {\n this.#state = 'faulted';\n this.#report({\n code: 'simulator/advance-failed',\n message: `Advancing ${delta} ps failed.`,\n cause,\n });\n throw cause;\n }\n }\n\n onPublish(listener: (publish: DevicePublish) => void): () => void {\n if (this.#state === 'disposed') {\n return () => {};\n }\n this.#publishListeners.add(listener);\n return () => {\n this.#publishListeners.delete(listener);\n };\n }\n\n deliver(target: DeviceTopicTarget, payload: unknown): void {\n const runtime = this.#requireRuntime();\n const instance = runtime.instances.get(target.symbol);\n if (instance === undefined) {\n throw new SimulatorError(\n 'simulator/unknown-target',\n `The schematic has no symbol \"${target.symbol}\".`,\n { symbol: target.symbol },\n );\n }\n const port = instance.description.ports.get(target.port);\n if (port === undefined) {\n throw new SimulatorError(\n 'simulator/unknown-port',\n `Device ${JSON.stringify(instance.deviceId)} has no port \"${target.port}\".`,\n { symbol: target.symbol },\n );\n }\n if (port.direction !== 'input') {\n throw new SimulatorError(\n 'simulator/unknown-port',\n `Port \"${target.port}\" of symbol \"${target.symbol}\" is an output, not an input.`,\n { symbol: target.symbol },\n );\n }\n this.#pending.push(Object.freeze({ target: Object.freeze({ ...target }), payload }));\n }\n\n reset(): void {\n this.#assertNotDisposed();\n if (this.#runtime === undefined) {\n this.#pending.length = 0;\n return;\n }\n // The modules are cached, so a reset never yields: it swaps in freshly built instances.\n const runtime = this.#buildRuntime(this.#schematic);\n this.#teardown();\n this.#runtime = runtime;\n this.#state = 'ready';\n }\n\n dispose(): void {\n if (this.#state === 'disposed') {\n return;\n }\n this.#state = 'disposed';\n this.#publishListeners.clear();\n this.#teardown();\n this.#modules.clear();\n }\n\n /** Loads the devices the schematic names; already loaded ids are reused. */\n async #loadModules(document: SchematicDocument): Promise<void> {\n for (const symbol of document.sheet.symbols) {\n if (this.#modules.has(symbol.device)) {\n continue;\n }\n try {\n this.#modules.set(\n symbol.device,\n await loadDeviceModule(symbol.device, this.#options.devices),\n );\n } catch (cause) {\n throw new SimulatorError(\n 'simulator/device-load-failed',\n `Device ${JSON.stringify(symbol.device)} of symbol \"${symbol.id}\" could not be loaded.`,\n { symbol: symbol.id, cause },\n );\n }\n }\n }\n\n /** Mounts every symbol of the schematic and runs the `initialize` pass on a fresh scheduler. */\n #buildRuntime(document: SchematicDocument): Runtime {\n const scheduler =\n this.#options.scheduler === undefined\n ? createScheduler()\n : createScheduler(this.#options.scheduler);\n const host = this.#options.host?.({ scheduler, schematic: document });\n const instances = new Map<string, DeviceInstance>();\n const topics: DeviceTopic[] = [];\n try {\n for (const symbol of document.sheet.symbols) {\n const instance = this.#createInstance(symbol, scheduler, host);\n instances.set(symbol.id, instance);\n collectTopics(symbol, instance, topics);\n }\n // Device factories only queue `initialize`; run it so the runtime is ready to advance.\n scheduler.start({ until: scheduler.now });\n return { scheduler, instances, topics: Object.freeze(topics), host };\n } catch (error) {\n for (const instance of instances.values()) {\n destroyQuietly(instance, (diagnostic) => this.#report(diagnostic));\n }\n disposeHostQuietly(host, (diagnostic) => this.#report(diagnostic));\n throw error;\n }\n }\n\n #createInstance(\n symbol: SchematicSymbol,\n scheduler: Scheduler,\n host: DeviceSimHost | undefined,\n ): DeviceInstance {\n const loaded = this.#modules.get(symbol.device);\n if (loaded === undefined) {\n throw new SimulatorError(\n 'simulator/device-load-failed',\n `Device ${JSON.stringify(symbol.device)} of symbol \"${symbol.id}\" was not loaded.`,\n { symbol: symbol.id },\n );\n }\n const additions = host?.instanceOptions?.(symbol);\n try {\n return createDeviceInstance(loaded, scheduler, {\n ...additions,\n instanceId: symbol.id,\n ...(symbol.properties === undefined ? {} : { properties: symbol.properties }),\n ...(host?.pinHost === undefined ? {} : { pinHost: host.pinHost }),\n ...(host?.topology === undefined ? {} : { connections: { topology: host.topology } }),\n onTopic: (message) => {\n this.#publish(scheduler, symbol.id, message.port, message.payload);\n },\n });\n } catch (cause) {\n throw new SimulatorError(\n 'simulator/instance-create-failed',\n `Symbol \"${symbol.id}\" could not be instantiated with device ${JSON.stringify(symbol.device)}.`,\n { symbol: symbol.id, cause },\n );\n }\n }\n\n #publish(scheduler: Scheduler, symbol: string, port: string, payload: TopicPayload): void {\n const publish: DevicePublish = Object.freeze({\n target: Object.freeze({ symbol, port }),\n payload,\n simTime: scheduler.now,\n });\n for (const listener of [...this.#publishListeners]) {\n try {\n listener(publish);\n } catch (cause) {\n this.#report({\n code: 'simulator/publish-failed',\n message: 'A publish listener threw.',\n symbol,\n cause,\n });\n }\n }\n }\n\n /** Hands the payloads queued by `deliver` to their instances at the slice's start time. */\n #flush(runtime: Runtime): void {\n if (this.#pending.length === 0) {\n return;\n }\n const deliveries = this.#pending.splice(0);\n const simTime = runtime.scheduler.now;\n for (const delivery of deliveries) {\n const instance = runtime.instances.get(delivery.target.symbol);\n if (instance === undefined) {\n continue; // The target was validated when it was queued; a reload dropped the instance.\n }\n // `deliver` accepts `unknown` because hosts forward widget payloads; the device layer\n // captures and validates the value against the port type.\n instance.deliver(delivery.target.port, delivery.payload as TopicPayload, { simTime });\n }\n }\n\n #teardown(): void {\n const runtime = this.#runtime;\n this.#runtime = undefined;\n this.#pending.length = 0;\n if (runtime === undefined) {\n return;\n }\n for (const instance of runtime.instances.values()) {\n destroyQuietly(instance, (diagnostic) => this.#report(diagnostic));\n }\n // The pin host and the topology outlive the instances they serve, so they go last.\n disposeHostQuietly(runtime.host, (diagnostic) => this.#report(diagnostic));\n }\n\n #requireRuntime(): Runtime {\n this.#assertNotDisposed();\n const runtime = this.#runtime;\n if (runtime === undefined) {\n throw new SimulatorError(\n 'simulator/not-ready',\n 'Load a schematic before advancing the simulation.',\n );\n }\n return runtime;\n }\n\n #assertNotDisposed(): void {\n if (this.#state === 'disposed') {\n throw new SimulatorError('simulator/disposed', 'The executor is disposed.');\n }\n }\n\n #report(diagnostic: SimulationDiagnostic): void {\n try {\n this.#options.onDiagnostic?.(diagnostic);\n } catch {\n // A throwing diagnostic sink must not take the simulation down.\n }\n }\n}\n\nfunction collectTopics(\n symbol: SchematicSymbol,\n instance: DeviceInstance,\n into: DeviceTopic[],\n): void {\n for (const [port, descriptor] of instance.description.ports) {\n into.push(\n Object.freeze({\n symbol: symbol.id,\n port,\n direction: descriptor.direction,\n type: descriptor.type,\n ...(descriptor.title === undefined ? {} : { title: descriptor.title }),\n }),\n );\n }\n}\n\nfunction destroyQuietly(\n instance: DeviceInstance,\n report: (diagnostic: SimulationDiagnostic) => void,\n): void {\n try {\n instance.destroy();\n } catch (cause) {\n report({\n code: 'simulator/dispose-failed',\n message: `Device instance \"${instance.instanceId}\" failed to destroy.`,\n symbol: instance.instanceId,\n cause,\n });\n }\n}\n\nfunction disposeHostQuietly(\n host: DeviceSimHost | undefined,\n report: (diagnostic: SimulationDiagnostic) => void,\n): void {\n try {\n host?.dispose?.();\n } catch (cause) {\n report({\n code: 'simulator/dispose-failed',\n message: 'The host wiring failed to dispose.',\n cause,\n });\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../src/errors.ts","../src/circuit.ts","../src/create-device-sim-executor.ts"],"sourcesContent":["export type SimulatorErrorCode =\n | 'simulator/disposed'\n | 'simulator/not-ready'\n | 'simulator/invalid-delta'\n | 'simulator/unknown-target'\n | 'simulator/unknown-port'\n | 'simulator/device-load-failed'\n | 'simulator/instance-create-failed';\n\nexport interface SimulatorErrorOptions {\n /** Symbol the failure belongs to, when it is attributable to one. */\n readonly symbol?: string;\n readonly cause?: unknown;\n}\n\n/** Error raised by the executor's host-facing calls. */\nexport class SimulatorError extends Error {\n override readonly name = 'SimulatorError';\n readonly code: SimulatorErrorCode;\n readonly symbol: string | undefined;\n\n constructor(code: SimulatorErrorCode, message: string, options: SimulatorErrorOptions = {}) {\n super(message, options.cause === undefined ? undefined : { cause: options.cause });\n this.code = code;\n this.symbol = options.symbol;\n }\n}\n","import {\n createPowerRuntime,\n type ElectricalPinInput,\n type PowerDefinition,\n type PowerDeviceDefinition,\n type TopologyConnectionDeclaration,\n type TopologyDefinition,\n type TopologyExternalWire,\n type TopologyPinDeclaration,\n} from '@mica-js/core';\nimport {\n deriveSchematicTopology,\n synthesizeDeviceSymbol,\n type DeviceSymbolProvider,\n type SchematicDocument,\n} from '@mica-js/core/schematic';\nimport type { LoadedDeviceModule } from '@mica-js/device';\nimport type { DeviceSimHost, DeviceSimHostFactory } from './types';\n\n/** Synthetic pin owner for the supplies the schematic's power/ground symbols name. */\nexport const SUPPLY_INSTANCE = 'power';\n\n/** Scheduler identity of the electrical network; distinct from every symbol id. */\nconst POWER_INSTANCE_ID = 'simulator/power';\n\n/** Every pin uses the same digital interpretation; module pins override mode and pull. */\nconst PIN_MODEL = Object.freeze({\n kind: 'ideal-voltage' as const,\n minVoltage: 0,\n maxVoltage: 5,\n lowVoltage: 0.8,\n highVoltage: 2,\n});\n\n/** Everything `createPowerRuntime` and the device instances need for one schematic. */\nexport interface CircuitLayout {\n readonly topology: TopologyDefinition;\n readonly power: PowerDefinition;\n /** Complete held-intent snapshot: one entry per declared pin. */\n readonly pinStates: readonly ElectricalPinInput[];\n}\n\nexport interface CircuitOptions {\n /** Voltage the schematic's power symbols hold, in volts. Defaults to 5. */\n readonly supplyVoltage?: number;\n}\n\n/**\n * Wiring host for schemes that are drawn, not described.\n *\n * Pass it as `DeviceSimExecutorOptions.host` and the executor mounts one electrical network per\n * runtime: the sheet's wires become nets, power/ground symbols become the supply rails, and every\n * device instance reads real pin voltages and connection states instead of host-recorded values.\n *\n * @example\n * createDeviceSimExecutor({ devices, host: createCircuitHost() });\n */\nexport function createCircuitHost(options: CircuitOptions = {}): DeviceSimHostFactory {\n return ({ scheduler, schematic, modules }) => {\n const circuit = buildCircuit(schematic, modules, options);\n if (circuit === undefined) {\n return {} satisfies DeviceSimHost;\n }\n const runtime = createPowerRuntime(scheduler, circuit.power, {\n instanceId: POWER_INSTANCE_ID,\n pinStates: circuit.pinStates,\n floating: { seed: 1 },\n });\n return {\n pinHost: runtime,\n topology: runtime,\n dispose: () => {\n runtime.destroy();\n },\n };\n };\n}\n\n/**\n * Turns a schematic into a topology plus power definition.\n *\n * Wire geometry is resolved by `deriveSchematicTopology` against synthesized symbols, so the\n * simulator never has to know pin coordinates. Every device pin becomes a topology pin, each net\n * becomes one external wire, and the device descriptions' connections become the switchable pairs.\n *\n * @returns `undefined` when the schematic declares no pins at all (nothing to solve).\n */\nexport function buildCircuit(\n document: SchematicDocument,\n modules: ReadonlyMap<string, LoadedDeviceModule>,\n options: CircuitOptions = {},\n): CircuitLayout | undefined {\n const supplyVoltage = options.supplyVoltage ?? 5;\n const derived = deriveSchematicTopology(document, { devices: createSymbolProvider(modules) });\n if (derived.pins.length === 0) {\n return undefined;\n }\n\n const kinds = pinKinds(document, modules);\n const pins: TopologyPinDeclaration[] = derived.pins.map((pin) => ({\n instanceId: pin.symbolId,\n pinName: pin.pinId,\n kind: kinds.get(`${pin.symbolId}\\u0000${pin.pinId}`) ?? 'signal',\n }));\n\n const wires: TopologyExternalWire[] = [];\n let supplyPin: string | undefined;\n let groundPin: string | undefined;\n\n for (const [index, net] of derived.nets.entries()) {\n const members = net.members\n .filter((member) => member.kind === 'pin')\n .map((member) => ({ instanceId: member.symbolId, pinName: member.pinId }));\n const ground = net.members.some((member) => member.kind === 'ground');\n const power = net.members.some((member) => member.kind === 'power');\n const name = net.name ?? `net-${index}`;\n\n if (power) {\n supplyPin ??= name;\n pins.push({ instanceId: SUPPLY_INSTANCE, pinName: name, kind: 'power' });\n members.push({ instanceId: SUPPLY_INSTANCE, pinName: name });\n }\n if (ground) {\n groundPin ??= name;\n pins.push({ instanceId: SUPPLY_INSTANCE, pinName: name, kind: 'gnd' });\n members.push({ instanceId: SUPPLY_INSTANCE, pinName: name });\n }\n if (members.length > 0) {\n wires.push({\n wireId: `net-${index}`,\n pins: members,\n ...(ground ? { globalGnd: true } : {}),\n });\n }\n }\n\n // A domain needs both rails; without them the network still resolves from the pin intents.\n const devices: PowerDeviceDefinition[] = [];\n if (supplyPin !== undefined && groundPin !== undefined) {\n devices.push({\n instanceId: SUPPLY_INSTANCE,\n powerDomains: {\n board: { supply: supplyPin, ground: groundPin, voltage: { turnOn: 3, turnOff: 2 } },\n },\n });\n }\n\n const topology: TopologyDefinition = {\n pins,\n wires,\n connections: deviceConnections(document, modules),\n };\n\n return {\n topology,\n power: { topology, devices },\n pinStates: pins.map((pin) => {\n const rail = pin.instanceId === SUPPLY_INSTANCE;\n if (rail && pin.kind === 'power') {\n // The supply rail is the schematic's only voltage source.\n return {\n instanceId: pin.instanceId,\n pinName: pin.pinName,\n powerReady: true,\n mode: 'analog' as const,\n outputEnabled: true,\n pull: 'none' as const,\n voltageIntent: supplyVoltage,\n model: PIN_MODEL,\n };\n }\n if (rail) {\n // Ground is the 0 V reference, so a contact to it really pulls a pin low.\n return {\n instanceId: pin.instanceId,\n pinName: pin.pinName,\n powerReady: true,\n mode: 'push-pull' as const,\n outputEnabled: true,\n pull: 'none' as const,\n digitalIntent: false,\n model: PIN_MODEL,\n };\n }\n return {\n instanceId: pin.instanceId,\n pinName: pin.pinName,\n powerReady: true,\n mode: 'high-z' as const,\n outputEnabled: false,\n pull: 'none' as const,\n model: PIN_MODEL,\n };\n }),\n };\n}\n\n/**\n * Symbols for every device the schematic names.\n *\n * The synthesized geometry must be the same one the `.sch` was authored against, so the name is\n * the full device id and pins keep their declared side; that is what the playground generator uses.\n */\nfunction createSymbolProvider(\n modules: ReadonlyMap<string, LoadedDeviceModule>,\n): DeviceSymbolProvider {\n const cache = new Map<string, ReturnType<typeof synthesizeDeviceSymbol> | undefined>();\n const symbolOf = (deviceId: string) => {\n if (!cache.has(deviceId)) {\n const module = modules.get(deviceId);\n cache.set(\n deviceId,\n module === undefined\n ? undefined\n : synthesizeDeviceSymbol({\n name: deviceId,\n pins: Object.fromEntries(\n [...module.description.pins.values()].map((pin) => [pin.key, { side: pin.side }]),\n ),\n }),\n );\n }\n return cache.get(deviceId);\n };\n return {\n getDeviceSymbol: (reference) => symbolOf(`${reference.packageId}:${reference.deviceId}`),\n getDeviceName: (reference) => `${reference.packageId}:${reference.deviceId}`,\n };\n}\n\nfunction pinKinds(\n document: SchematicDocument,\n modules: ReadonlyMap<string, LoadedDeviceModule>,\n): Map<string, TopologyPinDeclaration['kind']> {\n const kinds = new Map<string, TopologyPinDeclaration['kind']>();\n for (const symbol of document.sheet.symbols) {\n const module = modules.get(symbol.device);\n if (module === undefined) continue;\n for (const pin of module.description.pins.values()) {\n kinds.set(`${symbol.id}\\u0000${pin.key}`, pin.kind);\n }\n }\n return kinds;\n}\n\nfunction deviceConnections(\n document: SchematicDocument,\n modules: ReadonlyMap<string, LoadedDeviceModule>,\n): TopologyConnectionDeclaration[] {\n const connections: TopologyConnectionDeclaration[] = [];\n for (const symbol of document.sheet.symbols) {\n const module = modules.get(symbol.device);\n if (module === undefined) continue;\n for (const [connectionId, descriptor] of module.description.connections) {\n const [first, second] = descriptor.pins;\n if (first === undefined || second === undefined) continue;\n connections.push({\n instanceId: symbol.id,\n connectionId,\n pins: [first, second],\n initial: descriptor.initial,\n });\n }\n }\n return connections;\n}\n","import { createScheduler, timePs, type Scheduler, type TimePs } from '@mica-js/core';\nimport {\n designatorOf,\n symbolRole,\n type SchematicDocument,\n type SchematicSymbol,\n} from '@mica-js/core/schematic';\nimport {\n createDeviceInstance,\n loadDeviceModule,\n type DeviceInstance,\n type LoadedDeviceModule,\n type TopicPayload,\n} from '@mica-js/device';\nimport { SimulatorError } from './errors';\nimport type {\n AdvanceReport,\n DeviceSimExecutor,\n DeviceSimExecutorOptions,\n DeviceSimHost,\n DevicePublish,\n DeviceTopic,\n DeviceTopicTarget,\n SimulationDiagnostic,\n SimulationState,\n} from './types';\n\nconst EMPTY_SCHEMATIC: SchematicDocument = Object.freeze({\n version: 1,\n sheet: Object.freeze({\n symbols: Object.freeze([]),\n wires: Object.freeze([]),\n junctions: Object.freeze([]),\n }),\n});\n\nconst EMPTY_TOPICS: readonly DeviceTopic[] = Object.freeze([]);\n\n/** One loaded schematic: its scheduler, the instances keyed by symbol id and the topic index. */\ninterface Runtime {\n readonly scheduler: Scheduler;\n readonly instances: ReadonlyMap<string, DeviceInstance>;\n readonly topics: readonly DeviceTopic[];\n /** Wiring the instances were mounted against; released with the runtime. */\n readonly host: DeviceSimHost | undefined;\n}\n\ninterface PendingDelivery {\n readonly target: DeviceTopicTarget;\n readonly payload: unknown;\n}\n\n/**\n * Creates an executor for one schematic at a time.\n *\n * Every component symbol becomes a device instance on a private scheduler. Pin and connection\n * wiring comes from `options.host`, which is rebuilt for every runtime so it always belongs to the\n * scheduler that is running; schematic wiring itself is available as `createCircuitHost`.\n */\nexport function createDeviceSimExecutor(options: DeviceSimExecutorOptions): DeviceSimExecutor {\n return new DeviceSimExecutorImpl(options);\n}\n\nclass DeviceSimExecutorImpl implements DeviceSimExecutor {\n readonly #options: DeviceSimExecutorOptions;\n readonly #publishListeners = new Set<(publish: DevicePublish) => void>();\n readonly #pending: PendingDelivery[] = [];\n /** Loaded device modules, so `reset` can rebuild instances without touching the provider. */\n readonly #modules = new Map<string, LoadedDeviceModule>();\n #runtime: Runtime | undefined;\n #schematic: SchematicDocument = EMPTY_SCHEMATIC;\n #state: SimulationState = 'idle';\n\n constructor(options: DeviceSimExecutorOptions) {\n this.#options = options;\n }\n\n getState(): SimulationState {\n return this.#state;\n }\n\n getSimTime(): TimePs {\n return this.#runtime?.scheduler.now ?? timePs(0);\n }\n\n getSchematic(): SchematicDocument {\n return this.#schematic;\n }\n\n getTopics(): readonly DeviceTopic[] {\n return this.#runtime?.topics ?? EMPTY_TOPICS;\n }\n\n async loadSchematic(document: SchematicDocument): Promise<void> {\n this.#assertNotDisposed();\n await this.#loadModules(document);\n // Build first: a rejected load must leave the running simulation untouched.\n const runtime = this.#buildRuntime(document);\n this.#teardown();\n this.#runtime = runtime;\n this.#schematic = document;\n this.#state = 'ready';\n }\n\n advance(delta: TimePs): AdvanceReport {\n const runtime = this.#requireRuntime();\n if (!Number.isSafeInteger(delta) || delta <= 0) {\n throw new SimulatorError(\n 'simulator/invalid-delta',\n `advance expects a positive whole number of picoseconds, received ${String(delta)}.`,\n );\n }\n const { scheduler } = runtime;\n const from = scheduler.now;\n this.#flush(runtime);\n try {\n const result = scheduler.start({ until: from + delta });\n return Object.freeze({\n from,\n to: result.timePs,\n executed: result.executed,\n reason: result.reason,\n });\n } catch (cause) {\n this.#state = 'faulted';\n this.#report({\n code: 'simulator/advance-failed',\n message: `Advancing ${delta} ps failed.`,\n cause,\n });\n throw cause;\n }\n }\n\n onPublish(listener: (publish: DevicePublish) => void): () => void {\n if (this.#state === 'disposed') {\n return () => {};\n }\n this.#publishListeners.add(listener);\n return () => {\n this.#publishListeners.delete(listener);\n };\n }\n\n deliver(target: DeviceTopicTarget, payload: unknown): void {\n const runtime = this.#requireRuntime();\n const instance = runtime.instances.get(target.symbol);\n if (instance === undefined) {\n throw new SimulatorError(\n 'simulator/unknown-target',\n `The schematic has no symbol \"${target.symbol}\".`,\n { symbol: target.symbol },\n );\n }\n const port = instance.description.ports.get(target.port);\n if (port === undefined) {\n throw new SimulatorError(\n 'simulator/unknown-port',\n `Device ${JSON.stringify(instance.deviceId)} has no port \"${target.port}\".`,\n { symbol: target.symbol },\n );\n }\n if (port.direction !== 'input') {\n throw new SimulatorError(\n 'simulator/unknown-port',\n `Port \"${target.port}\" of symbol \"${target.symbol}\" is an output, not an input.`,\n { symbol: target.symbol },\n );\n }\n this.#pending.push(Object.freeze({ target: Object.freeze({ ...target }), payload }));\n }\n\n reset(): void {\n this.#assertNotDisposed();\n if (this.#runtime === undefined) {\n this.#pending.length = 0;\n return;\n }\n // The modules are cached, so a reset never yields: it swaps in freshly built instances.\n const runtime = this.#buildRuntime(this.#schematic);\n this.#teardown();\n this.#runtime = runtime;\n this.#state = 'ready';\n }\n\n dispose(): void {\n if (this.#state === 'disposed') {\n return;\n }\n this.#state = 'disposed';\n this.#publishListeners.clear();\n this.#teardown();\n this.#modules.clear();\n }\n\n /** Loads the devices the schematic names; already loaded ids are reused. */\n async #loadModules(document: SchematicDocument): Promise<void> {\n for (const symbol of document.sheet.symbols) {\n if (!isComponent(symbol) || this.#modules.has(symbol.device)) {\n continue;\n }\n try {\n this.#modules.set(\n symbol.device,\n await loadDeviceModule(symbol.device, this.#options.devices),\n );\n } catch (cause) {\n throw new SimulatorError(\n 'simulator/device-load-failed',\n `Device ${JSON.stringify(symbol.device)} of symbol \"${symbol.id}\" could not be loaded.`,\n { symbol: symbol.id, cause },\n );\n }\n }\n }\n\n /** Mounts every symbol of the schematic and runs the `initialize` pass on a fresh scheduler. */\n #buildRuntime(document: SchematicDocument): Runtime {\n const scheduler =\n this.#options.scheduler === undefined\n ? createScheduler()\n : createScheduler(this.#options.scheduler);\n const host = this.#options.host?.({\n scheduler,\n schematic: document,\n modules: this.#modules,\n });\n const instances = new Map<string, DeviceInstance>();\n const topics: DeviceTopic[] = [];\n try {\n for (const symbol of document.sheet.symbols) {\n if (!isComponent(symbol)) {\n continue; // Power, ground, label and text symbols only shape the nets.\n }\n const instance = this.#createInstance(symbol, scheduler, host);\n instances.set(symbol.id, instance);\n collectTopics(symbol, instance, topics);\n }\n // Device factories only queue `initialize`; run it so the runtime is ready to advance.\n scheduler.start({ until: scheduler.now });\n return { scheduler, instances, topics: Object.freeze(topics), host };\n } catch (error) {\n for (const instance of instances.values()) {\n destroyQuietly(instance, (diagnostic) => this.#report(diagnostic));\n }\n disposeHostQuietly(host, (diagnostic) => this.#report(diagnostic));\n throw error;\n }\n }\n\n #createInstance(\n symbol: SchematicSymbol,\n scheduler: Scheduler,\n host: DeviceSimHost | undefined,\n ): DeviceInstance {\n const loaded = this.#modules.get(symbol.device);\n if (loaded === undefined) {\n throw new SimulatorError(\n 'simulator/device-load-failed',\n `Device ${JSON.stringify(symbol.device)} of symbol \"${symbol.id}\" was not loaded.`,\n { symbol: symbol.id },\n );\n }\n const additions = host?.instanceOptions?.(symbol);\n try {\n return createDeviceInstance(loaded, scheduler, {\n ...additions,\n instanceId: symbol.id,\n ...(symbol.properties === undefined ? {} : { properties: symbol.properties }),\n ...(host?.pinHost === undefined ? {} : { pinHost: host.pinHost }),\n ...(host?.topology === undefined ? {} : { connections: { topology: host.topology } }),\n onTopic: (message) => {\n this.#publish(scheduler, symbol.id, message.port, message.payload);\n },\n });\n } catch (cause) {\n throw new SimulatorError(\n 'simulator/instance-create-failed',\n `Symbol \"${symbol.id}\" could not be instantiated with device ${JSON.stringify(symbol.device)}.`,\n { symbol: symbol.id, cause },\n );\n }\n }\n\n #publish(scheduler: Scheduler, symbol: string, port: string, payload: TopicPayload): void {\n const publish: DevicePublish = Object.freeze({\n target: Object.freeze({ symbol, port }),\n payload,\n simTime: scheduler.now,\n });\n for (const listener of [...this.#publishListeners]) {\n try {\n listener(publish);\n } catch (cause) {\n this.#report({\n code: 'simulator/publish-failed',\n message: 'A publish listener threw.',\n symbol,\n cause,\n });\n }\n }\n }\n\n /** Hands the payloads queued by `deliver` to their instances at the slice's start time. */\n #flush(runtime: Runtime): void {\n if (this.#pending.length === 0) {\n return;\n }\n const deliveries = this.#pending.splice(0);\n const simTime = runtime.scheduler.now;\n for (const delivery of deliveries) {\n const instance = runtime.instances.get(delivery.target.symbol);\n if (instance === undefined) {\n continue; // The target was validated when it was queued; a reload dropped the instance.\n }\n // `deliver` accepts `unknown` because hosts forward widget payloads; the device layer\n // captures and validates the value against the port type.\n instance.deliver(delivery.target.port, delivery.payload as TopicPayload, { simTime });\n }\n }\n\n #teardown(): void {\n const runtime = this.#runtime;\n this.#runtime = undefined;\n this.#pending.length = 0;\n if (runtime === undefined) {\n return;\n }\n for (const instance of runtime.instances.values()) {\n destroyQuietly(instance, (diagnostic) => this.#report(diagnostic));\n }\n // The pin host and the topology outlive the instances they serve, so they go last.\n disposeHostQuietly(runtime.host, (diagnostic) => this.#report(diagnostic));\n }\n\n #requireRuntime(): Runtime {\n this.#assertNotDisposed();\n const runtime = this.#runtime;\n if (runtime === undefined) {\n throw new SimulatorError(\n 'simulator/not-ready',\n 'Load a schematic before advancing the simulation.',\n );\n }\n return runtime;\n }\n\n #assertNotDisposed(): void {\n if (this.#state === 'disposed') {\n throw new SimulatorError('simulator/disposed', 'The executor is disposed.');\n }\n }\n\n #report(diagnostic: SimulationDiagnostic): void {\n try {\n this.#options.onDiagnostic?.(diagnostic);\n } catch {\n // A throwing diagnostic sink must not take the simulation down.\n }\n }\n}\n\n/** True when the symbol stands for a device the executor instantiates. */\nfunction isComponent(symbol: SchematicSymbol): boolean {\n return symbolRole(symbol.device) === 'component';\n}\n\nfunction collectTopics(\n symbol: SchematicSymbol,\n instance: DeviceInstance,\n into: DeviceTopic[],\n): void {\n const designator = designatorOf(symbol) ?? symbol.id;\n for (const [port, descriptor] of instance.description.ports) {\n into.push(\n Object.freeze({\n symbol: symbol.id,\n designator,\n port,\n direction: descriptor.direction,\n type: descriptor.type,\n ...(descriptor.title === undefined ? {} : { title: descriptor.title }),\n }),\n );\n }\n}\n\nfunction destroyQuietly(\n instance: DeviceInstance,\n report: (diagnostic: SimulationDiagnostic) => void,\n): void {\n try {\n instance.destroy();\n } catch (cause) {\n report({\n code: 'simulator/dispose-failed',\n message: `Device instance \"${instance.instanceId}\" failed to destroy.`,\n symbol: instance.instanceId,\n cause,\n });\n }\n}\n\nfunction disposeHostQuietly(\n host: DeviceSimHost | undefined,\n report: (diagnostic: SimulationDiagnostic) => void,\n): void {\n try {\n host?.dispose?.();\n } catch (cause) {\n report({\n code: 'simulator/dispose-failed',\n message: 'The host wiring failed to dispose.',\n cause,\n });\n }\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mica-js/simulator",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Mica simulator: runs the device instances of a schematic on one virtual clock",
5
5
  "license": "MIT",
6
6
  "repository": {