唯杰地图 logo唯杰地图
VJMAP
EN

模拟引擎与业务状态机模式

vjvp/references/sdk-app/sim-pattern.md 5.2 KB77 行
原文

返回 SKILL.md。下文模式提炼自官方参考实现(能源站;罐区+联锁演练+隐患/作业票)。

1. 分层:纯函数内核 + runtime 接线

points.ts     测点/设备/映射 单一事实源(initialPoints() 同时喂 connectionData 初始行与 sim 初值)
simulator.ts  物理模型:createSimState() + tickSim(state, dtSec, rng) → AlarmEvent[]   ←纯函数,可单测
alarms.ts     告警状态机:applyAlarmEvents / ackAlarm / alarmRows / alarmStats / alarmTrendRows ←纯函数
<domain>.ts   领域状态机(隐患生命周期 / 作业票流转 / 演练脚本)                        ←纯函数
runtime.ts    接线:setInterval(tick) → ctx.setData 各表;返回控制 API 给页面按钮

纯逻辑文件禁止 import vjvpsdk 的运行时值(import type 可以)——vitest 不依赖 SDK 构建产物。

2. 随机性:注入 rng,同种子可复现

export type Rng = () => number
export function mulberry32(seed: number): Rng { /* 标准 mulberry32 实现 */ }
// tick 内全部随机来自参数 rng;测试用恒值 rng(0.5=无事件无噪声)或首拍小值 rng 定向注入故障

3. 模拟量收敛(让数据"像真的")

  • approach(cur, target, rate):模拟量向"运行组合决定的目标值"收敛(流量∝运行泵数、温度∝运行冷机、功率=组合求和…),叠加 noise(amp)。
  • clamp 限界;输出用 round1/round2 收小数。
  • 缓慢往复量(液位)用方向变量 + 边界翻转 + 小概率随机换向。
  • 事件注入:运行设备小概率故障(faultLeft 计拍自愈);演练类(入侵/泄漏)提供 triggerXxx(state) 手动入口供演练按钮调用。

4. 告警规则:边沿触发(raise/resolve 各一次)

// state.activeRules: Record<string, boolean> 去抖
const hit = rule.hit(state)
if (hit && !state.activeRules[key]) { state.activeRules[key] = true; events.push({ type: 'raise', ruleKey: key, level, device, content }) }
else if (!hit && state.activeRules[key]) { delete state.activeRules[key]; events.push({ type: 'resolve', ruleKey: key }) }

告警状态机(alarms.ts)职责:同 ruleKey 活动期去重入列、resolve 置「已恢复」保留记录、人工 ack、容量上限丢最旧已恢复;派生视图 alarmRows(filter)/alarmStatRows/alarmTrendRows(now) 直接是表行形状。

5. 控制闭环与联锁

  • 控制 API:setDevice(state, id, run) 改状态 → 下一拍模拟量随之变化 → 组态/三维/图表全联动。runtime 里控制后立即补推一次(按钮反馈即时)。
  • 联锁/演练状态机:多阶段脚本(如 泄漏演练:normal → leaking(气体爬升) → 越限自动联锁(关阀停泵 + raise 严重告警 + 副作用回调如自动开应急窗) → recovering → normal)。阶段存在 state 上,tick 里推进;对外暴露 startDrill()。副作用(开窗/切页)不写在纯函数里——tick 返回事件,runtime 侧解释执行。

6. 数据建模五件套(connections 套路)

表 用途 形状
测点长表 tbl_pts 驱动组态/三维/地图元素(point-key-field/point-value-field 绑定,元素填 data-key) 两列:键/值(值可数值或 JSON 串——热力/人员定位整批刷新)
宽表快照 tbl_rt 2D 指标文本 value-field 绑定(取首行) 单行多列
台账表 表格/列表(设备/企业/风险点/作业票) 多行业务列(表格运行时给每行注入 _index=1 基行号,作 keyField/序号列数据源)
告警四表 告警表/等级分布/时段趋势/KPI 未处置数 tbl_alarm+tbl_alarm_stat+tbl_alarm_trend(+并入 KPI)
历史滚动 tbl_history 趋势图/趋势窗口 时间列 + N 指标列,固定窗口(如 48 行)滚动

派生视图函数(toPointRows/toRtRow/toKpiRow/toHistoryRow/...)输出列名必须与 connections 字段定义一致——写一致性单测锁住(见 §8)。

7. runtime 接线与推数节流

  • 主循环 setInterval(doTick, 2000);测点/快照/KPI 每拍推;告警/台账只在有事件时推;历史/慢表每 3 拍推一次并裁窗口。
  • 控制 API 对象(含 setDevice/ack/setFilter/startDrill/onXxxChange 订阅)由 startSim(ctx) 返回,build.ts 传给各页 builder。
  • 视图筛选(告警等级/台账关键字):筛选状态存 runtime,改变即重推该表——"代码筛选 + 全量重推"比在组件里做过滤简单可靠。

8. 单测配方(vitest)

[ ] 平稳运行:N 拍后模拟量在合理区间(恒值 rng)
[ ] 可复现:同种子两次跑 toPointRows 深等
[ ] 控制闭环:停设备 → 量值衰减 → raise 目标告警;恢复 → resolve
[ ] 事件注入:定向 rng 首拍注入故障/入侵 → 严重告警;复位/超时 → resolve
[ ] 状态机:状态流转合法(隐患 待整改→整改中→已闭环;作业票 待审批→作业中→已完工;非法流转拒绝)
[ ] 派生视图形状:toXxxRow 列名 ⊆ connections 字段(数据层一致性)
[ ] 场景一致性:pieces 引用的 data-key ⊆ initialPoints();连线端点图元存在;联动映射表目标 uid 存在