@mica-js/simulator 0.2.0 → 0.2.1
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 +79 -43
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -2,7 +2,27 @@
|
|
|
2
2
|
|
|
3
3
|
在一条虚拟时钟上运行原理图里的全部器件实例:`loadSchematic` 建实例,`advance` 推进时间,`onPublish` / `deliver` 与画布互投载荷。
|
|
4
4
|
|
|
5
|
-
依赖方向:`@mica-js/simulator` → `@mica-js/device
|
|
5
|
+
依赖方向:`@mica-js/simulator` → `@mica-js/device`,两者都建立在 `@mica-js/core`(及其底层的 `@mica-js/ir`)之上。
|
|
6
|
+
|
|
7
|
+
## 架构
|
|
8
|
+
|
|
9
|
+
```mermaid
|
|
10
|
+
flowchart TD
|
|
11
|
+
sch[".sch 原理图"] --> exec["DeviceSimExecutor"]
|
|
12
|
+
devices["IDeviceProvider<br/>器件描述 + 代码"] --> exec
|
|
13
|
+
|
|
14
|
+
exec --> scheduler["Scheduler<br/>一条虚拟时钟"]
|
|
15
|
+
exec --> instances["DeviceInstance × 符号"]
|
|
16
|
+
|
|
17
|
+
hostFactory["DeviceSimHostFactory<br/>每次运行时调用一次"] --> pinHost["pinHost"]
|
|
18
|
+
hostFactory --> topology["topology"]
|
|
19
|
+
pinHost --> instances
|
|
20
|
+
topology --> instances
|
|
21
|
+
|
|
22
|
+
exec --> onPublish["onPublish<br/>器件 → widget"]
|
|
23
|
+
exec --> deliver["deliver<br/>widget → 器件"]
|
|
24
|
+
circuit["createCircuitHost()<br/>可选电气接线"] -.-> hostFactory
|
|
25
|
+
```
|
|
6
26
|
|
|
7
27
|
## 用法
|
|
8
28
|
|
|
@@ -15,22 +35,18 @@ import { createDeviceSimExecutor } from '@mica-js/simulator';
|
|
|
15
35
|
import { createWidgetHost } from '@mica-js/widget';
|
|
16
36
|
|
|
17
37
|
const executor = createDeviceSimExecutor({
|
|
18
|
-
devices: provider, // IDeviceProvider
|
|
38
|
+
devices: provider, // IDeviceProvider:原理图里只写 device id
|
|
19
39
|
onDiagnostic: (diagnostic) => console.warn(diagnostic.code, diagnostic.message),
|
|
20
|
-
|
|
21
|
-
host: ({ scheduler, schematic }) => {
|
|
40
|
+
host: ({ scheduler }) => {
|
|
22
41
|
const power = createPowerRuntime(scheduler, board, { instanceId: 'power', pinStates });
|
|
23
42
|
return {
|
|
24
43
|
pinHost: power,
|
|
25
44
|
topology: power,
|
|
26
|
-
instanceOptions: (symbol) =>
|
|
27
|
-
symbol.device === 'mica:teaching-mcu' ? { updateResources: ['firmware'] } : undefined,
|
|
28
45
|
dispose: () => power.destroy(),
|
|
29
46
|
};
|
|
30
47
|
},
|
|
31
48
|
});
|
|
32
49
|
|
|
33
|
-
// 器件发布 → 绑定的 widget;widget 发布 → 绑定的器件端点。
|
|
34
50
|
const host = createWidgetHost({ provider, assets, simulation: executor });
|
|
35
51
|
|
|
36
52
|
await executor.loadSchematic(parseSchematic(schText)); // 文本由调用方解析
|
|
@@ -39,52 +55,74 @@ const report = executor.advance(timePs(100_000)); // 100 ns,同步完成
|
|
|
39
55
|
|
|
40
56
|
`DeviceSimExecutor` 结构上满足 `@mica-js/widget` 的 `WidgetHostSimulation`,不需要适配层;宿主之外也可以自己订阅 `onPublish` / 调用 `deliver`。
|
|
41
57
|
|
|
42
|
-
##
|
|
58
|
+
## 宿主接线
|
|
43
59
|
|
|
44
|
-
执行器不拥有电路:它只把宿主建的 `pinHost` 与 `topology`
|
|
60
|
+
执行器不拥有电路:它只把宿主建的 `pinHost` 与 `topology` 透传给每个实例。
|
|
45
61
|
|
|
46
|
-
| 字段 | 说明
|
|
47
|
-
| ----------------- |
|
|
48
|
-
| `pinHost` | 逐实例的 `pinHost`;`b.pin.*` / `b.observeDigital` 依赖它
|
|
49
|
-
| `topology` | 逐实例的 `connections.topology`;`b.connection.apply` 依赖它
|
|
50
|
-
| `instanceOptions` |
|
|
51
|
-
| `dispose` |
|
|
62
|
+
| 字段 | 说明 |
|
|
63
|
+
| ----------------- | ----------------------------------------------------------------------------- |
|
|
64
|
+
| `pinHost` | 逐实例的 `pinHost`;`b.pin.*` / `b.observeDigital` 依赖它 |
|
|
65
|
+
| `topology` | 逐实例的 `connections.topology`;`b.connection.apply` 依赖它 |
|
|
66
|
+
| `instanceOptions` | 逐符号补充实例选项(`updateResources` / `messaging`);身份、属性与接线不开放 |
|
|
67
|
+
| `dispose` | 实例全部销毁后调用,用来释放电源 / 拓扑运行时 |
|
|
52
68
|
|
|
53
|
-
工厂在每次 `loadSchematic` 和 `reset()` 建运行时各调用一次;每次运行时都有**自己的新调度器**,所以工厂里的实例 id
|
|
54
|
-
|
|
55
|
-
|
|
69
|
+
- 工厂在每次 `loadSchematic` 和 `reset()` 建运行时各调用一次;每次运行时都有**自己的新调度器**,所以工厂里的实例 id 不会跨运行时冲突。
|
|
70
|
+
- `DeviceSimHostContext` 提供 `scheduler`、`schematic` 与按 device id 索引的 `modules`,接线宿主直接读引脚几何而不必重复加载器件。
|
|
71
|
+
- `@mica-js/core` 的 `createPowerRuntime` 同时满足 `DevicePinHost` 与 `TopologyRuntime`,所以一块板只需要一个运行时。
|
|
72
|
+
- 工厂或 `dispose` 抛错时:构建阶段直接让 `loadSchematic` / `reset` 抛错(旧运行时原样保留),释放阶段转成 `simulator/dispose-failed` 诊断。
|
|
73
|
+
- 不配置 `host` 时实例没有 `pinHost` 也没有拓扑,用到引脚或连接的器件会在加载时报器件层错误,并被包成 `simulator/instance-create-failed`。
|
|
56
74
|
|
|
57
75
|
## 接口
|
|
58
76
|
|
|
59
|
-
| 成员 | 说明
|
|
60
|
-
| -------------------------- |
|
|
61
|
-
| `getState()` | `'idle'
|
|
62
|
-
| `getSimTime()` | 当前虚拟时间;首次加载前为 `0`
|
|
63
|
-
| `getSchematic()` | 当前运行时的来源文档;首次加载前是空图
|
|
64
|
-
| `getTopics()` |
|
|
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`
|
|
77
|
+
| 成员 | 说明 |
|
|
78
|
+
| -------------------------- | ---------------------------------------------------------------------- |
|
|
79
|
+
| `getState()` | `'idle'` / `'ready'` / `'faulted'` / `'disposed'`;只有 `ready` 能推进 |
|
|
80
|
+
| `getSimTime()` | 当前虚拟时间;首次加载前为 `0` |
|
|
81
|
+
| `getSchematic()` | 当前运行时的来源文档;首次加载前是空图 |
|
|
82
|
+
| `getTopics()` | 每个实例的 `symbol`(绑定用的就是它)、`designator`(位号)与 `port` |
|
|
83
|
+
| `loadSchematic(document)` | 按符号建实例并跑 `initialize`,时钟归零;构建失败时旧运行时原样保留 |
|
|
84
|
+
| `advance(delta)` | 同步推进 `delta` 皮秒,返回 `{ from, to, executed, reason }` |
|
|
85
|
+
| `onPublish(listener)` | 器件发布 `(target, payload, simTime)`;返回取消订阅函数 |
|
|
86
|
+
| `deliver(target, payload)` | 排入下一次 `advance` 的开头,同一时刻多次调用按调用顺序被器件看到 |
|
|
87
|
+
| `reset()` | 丢弃实例、按当前原理图重建,时钟归零;同步 |
|
|
88
|
+
| `dispose()` | 销毁全部实例并释放运行时;同步、幂等 |
|
|
89
|
+
|
|
90
|
+
`DeviceTopic` 的 `direction` / `type` 与 `DevicePortDescriptor` 同构,宿主可以直接用 `getTopics()` 生成画布的绑定候选。`symbol` 是原理图里的符号 id(绑定存的就是它),`designator` 是给人看的位号(符号自己的 `designator`,没写时回退到 id),两者在界面上不要混用。
|
|
73
91
|
|
|
74
92
|
## 语义
|
|
75
93
|
|
|
76
|
-
- **除 `loadSchematic` 之外全是同步的。** 加载之所以返回 promise,只因为器件模块要经 provider 取;模块随后被缓存,`reset()`
|
|
77
|
-
- **先建后换。** `loadSchematic` 在新调度器上建好实例、跑完 `initialize`
|
|
94
|
+
- **除 `loadSchematic` 之外全是同步的。** 加载之所以返回 promise,只因为器件模块要经 provider 取;模块随后被缓存,`reset()` 直接用缓存重建实例。仿真与 `WidgetHost` 跑在同一个 JS 线程上,没有 worker,也没有消息通道。
|
|
95
|
+
- **先建后换。** `loadSchematic` 在新调度器上建好实例、跑完 `initialize` 后才替换旧运行时,因此器件解析失败或工厂抛错时,正在跑的仿真不受影响,时钟也不回退。
|
|
78
96
|
- **`advance` 是同步的。** 整个时间片在调用内跑完,宿主可以在帧循环里直接调用并立刻拿到报告;推进失败时 `getState()` 变成 `'faulted'` 并原样抛出底层 `SchedulerError`。
|
|
79
|
-
- **`deliver` 收 `unknown`,延迟到时间片开头。**
|
|
97
|
+
- **`deliver` 收 `unknown`,延迟到时间片开头。** 端口类型由器件层校验;同一时刻多次投递按调用顺序被器件看到。载荷被器件层拒绝时 `advance` 抛出,但不会把执行器置为 `'faulted'`(故障只由调度器决定),宿主应当把这次输入记为被拒绝。
|
|
80
98
|
- **`simTime` 由执行器负责。** 器件发布时的时间戳取自调度器,画布侧不需要传时间。
|
|
81
99
|
- **诊断不中断仿真。** `onDiagnostic` 抛错会被吞掉;只有 `advance` 的调度器故障才会把状态置为 `'faulted'`。
|
|
82
100
|
|
|
83
|
-
|
|
101
|
+
```mermaid
|
|
102
|
+
sequenceDiagram
|
|
103
|
+
participant App as 宿主
|
|
104
|
+
participant Sim as DeviceSimExecutor
|
|
105
|
+
participant Sched as Scheduler
|
|
106
|
+
participant Dev as DeviceInstance
|
|
107
|
+
|
|
108
|
+
App->>Sim: deliver(target, payload)
|
|
109
|
+
Sim->>Sim: 入队待投递载荷
|
|
110
|
+
App->>Sim: advance(delta)
|
|
111
|
+
Sim->>Dev: deliver(port, payload, meta)
|
|
112
|
+
Sim->>Sched: start({ until: now + delta })
|
|
113
|
+
Sched->>Dev: initialize / 事件入口
|
|
114
|
+
Dev-->>Sim: api.publish / IR 输出
|
|
115
|
+
Sim-->>App: AdvanceReport
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
## 错误
|
|
119
|
+
|
|
120
|
+
- 诊断码以 `simulator/` 开头:`device-load-failed`、`instance-create-failed`、`advance-failed`、`publish-failed`、`dispose-failed`。
|
|
121
|
+
- 宿主调用的参数错误抛 `SimulatorError`,`code` 为 `simulator/disposed`、`simulator/not-ready`、`simulator/invalid-delta`、`simulator/unknown-target`、`simulator/unknown-port`、`simulator/device-load-failed` 或 `simulator/instance-create-failed`。
|
|
84
122
|
|
|
85
123
|
## 电气层
|
|
86
124
|
|
|
87
|
-
|
|
125
|
+
原理图里的导线就是电路。`createCircuitHost()` 返回一个宿主接线工厂,用 `deriveSchematicTopology` 把每个器件引脚按合成符号的几何位置落到网,折成 `TopologyDefinition` 交给 `createPowerRuntime` 解算,再把结果作为 `pinHost` / `topology` 交给执行器。
|
|
88
126
|
|
|
89
127
|
```ts
|
|
90
128
|
const executor = createDeviceSimExecutor({
|
|
@@ -94,15 +132,13 @@ const executor = createDeviceSimExecutor({
|
|
|
94
132
|
```
|
|
95
133
|
|
|
96
134
|
- **网 → 外部导线。** 每张网一条 `wireId`,成员是网里的全部引脚;带地符号的网标 `globalGnd`。
|
|
97
|
-
- **电源符号 → 电源实例。** `builtin:power` / `builtin:ground` 属于合成实例 `power`:供电网的引脚按 `supplyVoltage`(默认 5 V
|
|
98
|
-
- **器件引脚 → 拓扑引脚。** 引脚种类取自器件描述(`signal` / `power` / `gnd
|
|
135
|
+
- **电源符号 → 电源实例。** `builtin:power` / `builtin:ground` 属于合成实例 `power`:供电网的引脚按 `supplyVoltage`(默认 5 V)模拟输出,接地网的引脚按逻辑低电压强驱动。
|
|
136
|
+
- **器件引脚 → 拓扑引脚。** 引脚种类取自器件描述(`signal` / `power` / `gnd`);描述里的 `connections` 变成可开关的内部连接,`connection.apply` 直接作用在这张网上。
|
|
99
137
|
- **引脚初值一条不少。** `PowerOptions.pinStates` 要求每个已声明引脚一条,默认高阻、无上下拉;器件在 `initialize` 里 `pin.configure` 之后由器件层覆盖。
|
|
100
138
|
- **生命周期跟着运行时走。** 实例先销毁(退订、停观察者),工厂的 `dispose` 最后销毁电气网络;构建失败时同样按这个顺序回收。
|
|
101
139
|
|
|
102
|
-
按钮 + 计数器 + 数码管的完整样例见 `apps/playground/public/devices/`,`apps/playground/scripts/build-device-demo.mjs` 负责在改动布局后重算导线端点。
|
|
103
|
-
|
|
104
140
|
## 边界
|
|
105
141
|
|
|
106
|
-
- **电路定义归宿主所有。** 执行器只透传 `pinHost` / `topology
|
|
142
|
+
- **电路定义归宿主所有。** 执行器只透传 `pinHost` / `topology`,不解析板级电源、上拉或网络拓扑;引脚与电气观测由宿主读自己的运行时。`createCircuitHost` 是可选的实现之一,手写电路可以直接实现 `DeviceSimHostFactory`。
|
|
107
143
|
- **模拟量只有理想电压源。** `createCircuitHost` 的引脚模型固定为 `ideal-voltage`(0–5 V,低 0.8 V、高 2 V),没有电流、RC 或器件间负载;需要更细的电气行为时要往 `@mica-js/core` 的电气层加模型,或换一个自定义宿主。
|
|
108
|
-
-
|
|
144
|
+
- **器件代码在宿主 realm 里求值**(与 `loadDeviceModule` 相同,等同可信扩展),不做沙箱化。
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mica-js/simulator",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "Mica simulator: runs the device instances of a schematic on one virtual clock",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -23,8 +23,8 @@
|
|
|
23
23
|
"access": "public"
|
|
24
24
|
},
|
|
25
25
|
"dependencies": {
|
|
26
|
-
"@mica-js/core": "0.
|
|
27
|
-
"@mica-js/device": "0.2.
|
|
26
|
+
"@mica-js/core": "0.3.0",
|
|
27
|
+
"@mica-js/device": "0.2.1"
|
|
28
28
|
},
|
|
29
29
|
"devDependencies": {
|
|
30
30
|
"typescript": "^6.0.3",
|