@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 +35 -15
- package/dist/index.d.ts +45 -4
- package/dist/index.js +225 -7
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
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`
|
|
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
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
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
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
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":";;;;;AAgBA,IAAa,iBAAb,cAAoC,MAAM;CACxC,OAAyB;CACzB;CACA;CAEA,YAAY,MAA0B,SAAiB,UAAiC,CAAC,GAAG;EAC1F,MAAM,SAAS,QAAQ,UAAU,KAAA,IAAY,KAAA,IAAY,EAAE,OAAO,QAAQ,MAAM,CAAC;EACjF,KAAK,OAAO;EACZ,KAAK,SAAS,QAAQ;CACxB;AACF;;;;ACNA,IAAa,kBAAkB;;AAG/B,IAAM,oBAAoB;;AAG1B,IAAM,YAAY,OAAO,OAAO;CAC9B,MAAM;CACN,YAAY;CACZ,YAAY;CACZ,YAAY;CACZ,aAAa;AACf,CAAC;;;;;;;;;;;AAyBD,SAAgB,kBAAkB,UAA0B,CAAC,GAAyB;CACpF,QAAQ,EAAE,WAAW,WAAW,cAAc;EAC5C,MAAM,UAAU,aAAa,WAAW,SAAS,OAAO;EACxD,IAAI,YAAY,KAAA,GACd,OAAO,CAAC;EAEV,MAAM,UAAU,mBAAmB,WAAW,QAAQ,OAAO;GAC3D,YAAY;GACZ,WAAW,QAAQ;GACnB,UAAU,EAAE,MAAM,EAAE;EACtB,CAAC;EACD,OAAO;GACL,SAAS;GACT,UAAU;GACV,eAAe;IACb,QAAQ,QAAQ;GAClB;EACF;CACF;AACF;;;;;;;;;;AAWA,SAAgB,aACd,UACA,SACA,UAA0B,CAAC,GACA;CAC3B,MAAM,gBAAgB,QAAQ,iBAAiB;CAC/C,MAAM,UAAU,wBAAwB,UAAU,EAAE,SAAS,qBAAqB,OAAO,EAAE,CAAC;CAC5F,IAAI,QAAQ,KAAK,WAAW,GAC1B;CAGF,MAAM,QAAQ,SAAS,UAAU,OAAO;CACxC,MAAM,OAAiC,QAAQ,KAAK,KAAK,SAAS;EAChE,YAAY,IAAI;EAChB,SAAS,IAAI;EACb,MAAM,MAAM,IAAI,GAAG,IAAI,SAAS,QAAQ,IAAI,OAAO,KAAK;CAC1D,EAAE;CAEF,MAAM,QAAgC,CAAC;CACvC,IAAI;CACJ,IAAI;CAEJ,KAAK,MAAM,CAAC,OAAO,QAAQ,QAAQ,KAAK,QAAQ,GAAG;EACjD,MAAM,UAAU,IAAI,QACjB,QAAQ,WAAW,OAAO,SAAS,KAAK,CAAC,CACzC,KAAK,YAAY;GAAE,YAAY,OAAO;GAAU,SAAS,OAAO;EAAM,EAAE;EAC3E,MAAM,SAAS,IAAI,QAAQ,MAAM,WAAW,OAAO,SAAS,QAAQ;EACpE,MAAM,QAAQ,IAAI,QAAQ,MAAM,WAAW,OAAO,SAAS,OAAO;EAClE,MAAM,OAAO,IAAI,QAAQ,OAAO;EAEhC,IAAI,OAAO;GACT,cAAc;GACd,KAAK,KAAK;IAAE,YAAY;IAAiB,SAAS;IAAM,MAAM;GAAQ,CAAC;GACvE,QAAQ,KAAK;IAAE,YAAY;IAAiB,SAAS;GAAK,CAAC;EAC7D;EACA,IAAI,QAAQ;GACV,cAAc;GACd,KAAK,KAAK;IAAE,YAAY;IAAiB,SAAS;IAAM,MAAM;GAAM,CAAC;GACrE,QAAQ,KAAK;IAAE,YAAY;IAAiB,SAAS;GAAK,CAAC;EAC7D;EACA,IAAI,QAAQ,SAAS,GACnB,MAAM,KAAK;GACT,QAAQ,OAAO;GACf,MAAM;GACN,GAAI,SAAS,EAAE,WAAW,KAAK,IAAI,CAAC;EACtC,CAAC;CAEL;CAGA,MAAM,UAAmC,CAAC;CAC1C,IAAI,cAAc,KAAA,KAAa,cAAc,KAAA,GAC3C,QAAQ,KAAK;EACX,YAAY;EACZ,cAAc,EACZ,OAAO;GAAE,QAAQ;GAAW,QAAQ;GAAW,SAAS;IAAE,QAAQ;IAAG,SAAS;GAAE;EAAE,EACpF;CACF,CAAC;CAGH,MAAM,WAA+B;EACnC;EACA;EACA,aAAa,kBAAkB,UAAU,OAAO;CAClD;CAEA,OAAO;EACL;EACA,OAAO;GAAE;GAAU;EAAQ;EAC3B,WAAW,KAAK,KAAK,QAAQ;GAC3B,MAAM,OAAO,IAAI,eAAe;GAChC,IAAI,QAAQ,IAAI,SAAS,SAEvB,OAAO;IACL,YAAY,IAAI;IAChB,SAAS,IAAI;IACb,YAAY;IACZ,MAAM;IACN,eAAe;IACf,MAAM;IACN,eAAe;IACf,OAAO;GACT;GAEF,IAAI,MAEF,OAAO;IACL,YAAY,IAAI;IAChB,SAAS,IAAI;IACb,YAAY;IACZ,MAAM;IACN,eAAe;IACf,MAAM;IACN,eAAe;IACf,OAAO;GACT;GAEF,OAAO;IACL,YAAY,IAAI;IAChB,SAAS,IAAI;IACb,YAAY;IACZ,MAAM;IACN,eAAe;IACf,MAAM;IACN,OAAO;GACT;EACF,CAAC;CACH;AACF;;;;;;;AAQA,SAAS,qBACP,SACsB;CACtB,MAAM,wBAAQ,IAAI,IAAmE;CACrF,MAAM,YAAY,aAAqB;EACrC,IAAI,CAAC,MAAM,IAAI,QAAQ,GAAG;GACxB,MAAM,SAAS,QAAQ,IAAI,QAAQ;GACnC,MAAM,IACJ,UACA,WAAW,KAAA,IACP,KAAA,IACA,uBAAuB;IACrB,MAAM;IACN,MAAM,OAAO,YACX,CAAC,GAAG,OAAO,YAAY,KAAK,OAAO,CAAC,CAAC,CAAC,KAAK,QAAQ,CAAC,IAAI,KAAK,EAAE,MAAM,IAAI,KAAK,CAAC,CAAC,CAClF;GACF,CAAC,CACP;EACF;EACA,OAAO,MAAM,IAAI,QAAQ;CAC3B;CACA,OAAO;EACL,kBAAkB,cAAc,SAAS,GAAG,UAAU,UAAU,GAAG,UAAU,UAAU;EACvF,gBAAgB,cAAc,GAAG,UAAU,UAAU,GAAG,UAAU;CACpE;AACF;AAEA,SAAS,SACP,UACA,SAC6C;CAC7C,MAAM,wBAAQ,IAAI,IAA4C;CAC9D,KAAK,MAAM,UAAU,SAAS,MAAM,SAAS;EAC3C,MAAM,SAAS,QAAQ,IAAI,OAAO,MAAM;EACxC,IAAI,WAAW,KAAA,GAAW;EAC1B,KAAK,MAAM,OAAO,OAAO,YAAY,KAAK,OAAO,GAC/C,MAAM,IAAI,GAAG,OAAO,GAAG,QAAQ,IAAI,OAAO,IAAI,IAAI;CAEtD;CACA,OAAO;AACT;AAEA,SAAS,kBACP,UACA,SACiC;CACjC,MAAM,cAA+C,CAAC;CACtD,KAAK,MAAM,UAAU,SAAS,MAAM,SAAS;EAC3C,MAAM,SAAS,QAAQ,IAAI,OAAO,MAAM;EACxC,IAAI,WAAW,KAAA,GAAW;EAC1B,KAAK,MAAM,CAAC,cAAc,eAAe,OAAO,YAAY,aAAa;GACvE,MAAM,CAAC,OAAO,UAAU,WAAW;GACnC,IAAI,UAAU,KAAA,KAAa,WAAW,KAAA,GAAW;GACjD,YAAY,KAAK;IACf,YAAY,OAAO;IACnB;IACA,MAAM,CAAC,OAAO,MAAM;IACpB,SAAS,WAAW;GACtB,CAAC;EACH;CACF;CACA,OAAO;AACT;;;AC9OA,IAAM,kBAAqC,OAAO,OAAO;CACvD,SAAS;CACT,OAAO,OAAO,OAAO;EACnB,SAAS,OAAO,OAAO,CAAC,CAAC;EACzB,OAAO,OAAO,OAAO,CAAC,CAAC;EACvB,WAAW,OAAO,OAAO,CAAC,CAAC;CAC7B,CAAC;AACH,CAAC;AAED,IAAM,eAAuC,OAAO,OAAO,CAAC,CAAC;;;;;;;;AAuB7D,SAAgB,wBAAwB,SAAsD;CAC5F,OAAO,IAAI,sBAAsB,OAAO;AAC1C;AAEA,IAAM,wBAAN,MAAyD;CACvD;CACA,oCAA6B,IAAI,IAAsC;CACvE,WAAuC,CAAC;;CAExC,2BAAoB,IAAI,IAAgC;CACxD;CACA,aAAgC;CAChC,SAA0B;CAE1B,YAAY,SAAmC;EAC7C,KAAK,WAAW;CAClB;CAEA,WAA4B;EAC1B,OAAO,KAAK;CACd;CAEA,aAAqB;EACnB,OAAO,KAAK,UAAU,UAAU,OAAO,OAAO,CAAC;CACjD;CAEA,eAAkC;EAChC,OAAO,KAAK;CACd;CAEA,YAAoC;EAClC,OAAO,KAAK,UAAU,UAAU;CAClC;CAEA,MAAM,cAAc,UAA4C;EAC9D,KAAK,mBAAmB;EACxB,MAAM,KAAK,aAAa,QAAQ;EAEhC,MAAM,UAAU,KAAK,cAAc,QAAQ;EAC3C,KAAK,UAAU;EACf,KAAK,WAAW;EAChB,KAAK,aAAa;EAClB,KAAK,SAAS;CAChB;CAEA,QAAQ,OAA8B;EACpC,MAAM,UAAU,KAAK,gBAAgB;EACrC,IAAI,CAAC,OAAO,cAAc,KAAK,KAAK,SAAS,GAC3C,MAAM,IAAI,eACR,2BACA,oEAAoE,OAAO,KAAK,EAAE,EACpF;EAEF,MAAM,EAAE,cAAc;EACtB,MAAM,OAAO,UAAU;EACvB,KAAK,OAAO,OAAO;EACnB,IAAI;GACF,MAAM,SAAS,UAAU,MAAM,EAAE,OAAO,OAAO,MAAM,CAAC;GACtD,OAAO,OAAO,OAAO;IACnB;IACA,IAAI,OAAO;IACX,UAAU,OAAO;IACjB,QAAQ,OAAO;GACjB,CAAC;EACH,SAAS,OAAO;GACd,KAAK,SAAS;GACd,KAAK,QAAQ;IACX,MAAM;IACN,SAAS,aAAa,MAAM;IAC5B;GACF,CAAC;GACD,MAAM;EACR;CACF;CAEA,UAAU,UAAwD;EAChE,IAAI,KAAK,WAAW,YAClB,aAAa,CAAC;EAEhB,KAAK,kBAAkB,IAAI,QAAQ;EACnC,aAAa;GACX,KAAK,kBAAkB,OAAO,QAAQ;EACxC;CACF;CAEA,QAAQ,QAA2B,SAAwB;EAEzD,MAAM,WADU,KAAK,gBACJ,CAAA,CAAQ,UAAU,IAAI,OAAO,MAAM;EACpD,IAAI,aAAa,KAAA,GACf,MAAM,IAAI,eACR,4BACA,gCAAgC,OAAO,OAAO,KAC9C,EAAE,QAAQ,OAAO,OAAO,CAC1B;EAEF,MAAM,OAAO,SAAS,YAAY,MAAM,IAAI,OAAO,IAAI;EACvD,IAAI,SAAS,KAAA,GACX,MAAM,IAAI,eACR,0BACA,UAAU,KAAK,UAAU,SAAS,QAAQ,EAAE,gBAAgB,OAAO,KAAK,KACxE,EAAE,QAAQ,OAAO,OAAO,CAC1B;EAEF,IAAI,KAAK,cAAc,SACrB,MAAM,IAAI,eACR,0BACA,SAAS,OAAO,KAAK,eAAe,OAAO,OAAO,gCAClD,EAAE,QAAQ,OAAO,OAAO,CAC1B;EAEF,KAAK,SAAS,KAAK,OAAO,OAAO;GAAE,QAAQ,OAAO,OAAO,EAAE,GAAG,OAAO,CAAC;GAAG;EAAQ,CAAC,CAAC;CACrF;CAEA,QAAc;EACZ,KAAK,mBAAmB;EACxB,IAAI,KAAK,aAAa,KAAA,GAAW;GAC/B,KAAK,SAAS,SAAS;GACvB;EACF;EAEA,MAAM,UAAU,KAAK,cAAc,KAAK,UAAU;EAClD,KAAK,UAAU;EACf,KAAK,WAAW;EAChB,KAAK,SAAS;CAChB;CAEA,UAAgB;EACd,IAAI,KAAK,WAAW,YAClB;EAEF,KAAK,SAAS;EACd,KAAK,kBAAkB,MAAM;EAC7B,KAAK,UAAU;EACf,KAAK,SAAS,MAAM;CACtB;;CAGA,MAAM,aAAa,UAA4C;EAC7D,KAAK,MAAM,UAAU,SAAS,MAAM,SAAS;GAC3C,IAAI,CAAC,YAAY,MAAM,KAAK,KAAK,SAAS,IAAI,OAAO,MAAM,GACzD;GAEF,IAAI;IACF,KAAK,SAAS,IACZ,OAAO,QACP,MAAM,iBAAiB,OAAO,QAAQ,KAAK,SAAS,OAAO,CAC7D;GACF,SAAS,OAAO;IACd,MAAM,IAAI,eACR,gCACA,UAAU,KAAK,UAAU,OAAO,MAAM,EAAE,cAAc,OAAO,GAAG,yBAChE;KAAE,QAAQ,OAAO;KAAI;IAAM,CAC7B;GACF;EACF;CACF;;CAGA,cAAc,UAAsC;EAClD,MAAM,YACJ,KAAK,SAAS,cAAc,KAAA,IACxB,gBAAgB,IAChB,gBAAgB,KAAK,SAAS,SAAS;EAC7C,MAAM,OAAO,KAAK,SAAS,OAAO;GAChC;GACA,WAAW;GACX,SAAS,KAAK;EAChB,CAAC;EACD,MAAM,4BAAY,IAAI,IAA4B;EAClD,MAAM,SAAwB,CAAC;EAC/B,IAAI;GACF,KAAK,MAAM,UAAU,SAAS,MAAM,SAAS;IAC3C,IAAI,CAAC,YAAY,MAAM,GACrB;IAEF,MAAM,WAAW,KAAK,gBAAgB,QAAQ,WAAW,IAAI;IAC7D,UAAU,IAAI,OAAO,IAAI,QAAQ;IACjC,cAAc,QAAQ,UAAU,MAAM;GACxC;GAEA,UAAU,MAAM,EAAE,OAAO,UAAU,IAAI,CAAC;GACxC,OAAO;IAAE;IAAW;IAAW,QAAQ,OAAO,OAAO,MAAM;IAAG;GAAK;EACrE,SAAS,OAAO;GACd,KAAK,MAAM,YAAY,UAAU,OAAO,GACtC,eAAe,WAAW,eAAe,KAAK,QAAQ,UAAU,CAAC;GAEnE,mBAAmB,OAAO,eAAe,KAAK,QAAQ,UAAU,CAAC;GACjE,MAAM;EACR;CACF;CAEA,gBACE,QACA,WACA,MACgB;EAChB,MAAM,SAAS,KAAK,SAAS,IAAI,OAAO,MAAM;EAC9C,IAAI,WAAW,KAAA,GACb,MAAM,IAAI,eACR,gCACA,UAAU,KAAK,UAAU,OAAO,MAAM,EAAE,cAAc,OAAO,GAAG,oBAChE,EAAE,QAAQ,OAAO,GAAG,CACtB;EAEF,MAAM,YAAY,MAAM,kBAAkB,MAAM;EAChD,IAAI;GACF,OAAO,qBAAqB,QAAQ,WAAW;IAC7C,GAAG;IACH,YAAY,OAAO;IACnB,GAAI,OAAO,eAAe,KAAA,IAAY,CAAC,IAAI,EAAE,YAAY,OAAO,WAAW;IAC3E,GAAI,MAAM,YAAY,KAAA,IAAY,CAAC,IAAI,EAAE,SAAS,KAAK,QAAQ;IAC/D,GAAI,MAAM,aAAa,KAAA,IAAY,CAAC,IAAI,EAAE,aAAa,EAAE,UAAU,KAAK,SAAS,EAAE;IACnF,UAAU,YAAY;KACpB,KAAK,SAAS,WAAW,OAAO,IAAI,QAAQ,MAAM,QAAQ,OAAO;IACnE;GACF,CAAC;EACH,SAAS,OAAO;GACd,MAAM,IAAI,eACR,oCACA,WAAW,OAAO,GAAG,0CAA0C,KAAK,UAAU,OAAO,MAAM,EAAE,IAC7F;IAAE,QAAQ,OAAO;IAAI;GAAM,CAC7B;EACF;CACF;CAEA,SAAS,WAAsB,QAAgB,MAAc,SAA6B;EACxF,MAAM,UAAyB,OAAO,OAAO;GAC3C,QAAQ,OAAO,OAAO;IAAE;IAAQ;GAAK,CAAC;GACtC;GACA,SAAS,UAAU;EACrB,CAAC;EACD,KAAK,MAAM,YAAY,CAAC,GAAG,KAAK,iBAAiB,GAC/C,IAAI;GACF,SAAS,OAAO;EAClB,SAAS,OAAO;GACd,KAAK,QAAQ;IACX,MAAM;IACN,SAAS;IACT;IACA;GACF,CAAC;EACH;CAEJ;;CAGA,OAAO,SAAwB;EAC7B,IAAI,KAAK,SAAS,WAAW,GAC3B;EAEF,MAAM,aAAa,KAAK,SAAS,OAAO,CAAC;EACzC,MAAM,UAAU,QAAQ,UAAU;EAClC,KAAK,MAAM,YAAY,YAAY;GACjC,MAAM,WAAW,QAAQ,UAAU,IAAI,SAAS,OAAO,MAAM;GAC7D,IAAI,aAAa,KAAA,GACf;GAIF,SAAS,QAAQ,SAAS,OAAO,MAAM,SAAS,SAAyB,EAAE,QAAQ,CAAC;EACtF;CACF;CAEA,YAAkB;EAChB,MAAM,UAAU,KAAK;EACrB,KAAK,WAAW,KAAA;EAChB,KAAK,SAAS,SAAS;EACvB,IAAI,YAAY,KAAA,GACd;EAEF,KAAK,MAAM,YAAY,QAAQ,UAAU,OAAO,GAC9C,eAAe,WAAW,eAAe,KAAK,QAAQ,UAAU,CAAC;EAGnE,mBAAmB,QAAQ,OAAO,eAAe,KAAK,QAAQ,UAAU,CAAC;CAC3E;CAEA,kBAA2B;EACzB,KAAK,mBAAmB;EACxB,MAAM,UAAU,KAAK;EACrB,IAAI,YAAY,KAAA,GACd,MAAM,IAAI,eACR,uBACA,mDACF;EAEF,OAAO;CACT;CAEA,qBAA2B;EACzB,IAAI,KAAK,WAAW,YAClB,MAAM,IAAI,eAAe,sBAAsB,2BAA2B;CAE9E;CAEA,QAAQ,YAAwC;EAC9C,IAAI;GACF,KAAK,SAAS,eAAe,UAAU;EACzC,QAAQ,CAER;CACF;AACF;;AAGA,SAAS,YAAY,QAAkC;CACrD,OAAO,WAAW,OAAO,MAAM,MAAM;AACvC;AAEA,SAAS,cACP,QACA,UACA,MACM;CACN,MAAM,aAAa,aAAa,MAAM,KAAK,OAAO;CAClD,KAAK,MAAM,CAAC,MAAM,eAAe,SAAS,YAAY,OACpD,KAAK,KACH,OAAO,OAAO;EACZ,QAAQ,OAAO;EACf;EACA;EACA,WAAW,WAAW;EACtB,MAAM,WAAW;EACjB,GAAI,WAAW,UAAU,KAAA,IAAY,CAAC,IAAI,EAAE,OAAO,WAAW,MAAM;CACtE,CAAC,CACH;AAEJ;AAEA,SAAS,eACP,UACA,QACM;CACN,IAAI;EACF,SAAS,QAAQ;CACnB,SAAS,OAAO;EACd,OAAO;GACL,MAAM;GACN,SAAS,oBAAoB,SAAS,WAAW;GACjD,QAAQ,SAAS;GACjB;EACF,CAAC;CACH;AACF;AAEA,SAAS,mBACP,MACA,QACM;CACN,IAAI;EACF,MAAM,UAAU;CAClB,SAAS,OAAO;EACd,OAAO;GACL,MAAM;GACN,SAAS;GACT;EACF,CAAC;CACH;AACF"}
|