Plant Simulation Research Bridge
# Plant Simulation Research Bridge (psbridge)
面向可重复调度实验的 Plant Simulation 控制层。它不是把两个上游 MCP 项目的
61 个工具拼在一起,而是围绕一个更窄、更严格的目标重建:
给定调度实例与派工策略 -> 在隔离模型副本上批量运行 -> 标准化指标
-> 独立实现交叉核验 -> 可复现的结果记录
## 当前状态(必须如实阅读)
| 部分 | 状态 | 依据 |
| --- | --- | --- |
| 实例模型、派工策略、参考 oracle、指标、有效性门禁、实验运行与落盘 | 已验证 | 完整 pytest 通过,含引擎与独立 oracle 的随机实例交叉核验 |
| COM 网关、安全操作层、Fake 后端 | 已验证(Fake 后端) | tests/test_com_executor.py、test_com_fake_backend.py、test_core_ops.py、test_core_validation.py |
| Plant Simulation 13 的 COM 表面 | **已实测** | 30 个有界探测,证据在 ../evidence/plant-sim-bridge/real-machine-probe/ |
| Win32 COM 后端(按实测修正后) | 已验证(基础 COM)/ 局部桩测 | 连接/加载/创建/读写/控制/连线/MU/指标 VERIFIED;与模型会话相关的边界修复在 tests/ 中用 COM 桩测试 |
| 实机单线闭环与已实测 SimTalk 生成器 | **已验证** | 1000 s 跑出 100 件三次完全一致;`dispatch.py` / `jobshop.py` 已实机通过;离线模板 `psbridge/simtalk/templates.py` 仍标 `NOT_VERIFIED` |
| 排程驱动投料 + 多机选路 | **已验证** | 3 作业 / 2 机:18 件全部产出并排空,工时与利用率逐项手算对得上;`scripts/real_machine_dispatch_smoke.py` |
| 多工序作业车间(DFJSP 路线原型) | **已验证(有边界)** | 2 作业 x 2 工序 x 2 机交叉工艺(工序间缓冲防死锁):40 件全部按路线走完并排空,M1 120 s / M2 200 s 与工序数完全对账;`scripts/real_machine_jobshop_smoke.py` |
| MCP 传输层(stdio JSON-RPC 2.0)与工具层 | **已验证** | 共注册 30 个工具(9 个实验/SimTalk 工具 + 21 个模型/仿真/方法工具,模型会话通过单 STA `ComExecutor` 串行化);`psbridge/mcp/server.py` 经 `scripts/mcp_stdio_smoke.py` 真子进程校验通过 |
**没有实机验收之前,任何涉及 Plant Simulation 的结论都不能作为论文证据。**
这不是免责声明,而是本项目的有效性门禁设计的一部分:每份结果记录都带有
backend 与 engine_kind 字段,假引擎的结果永远不会被误认为实机结果。
## 实机实测结论(2026-10-28)
本机**确实安装了** Tecnomatix Plant Simulation 13。已用 30 个有界探测实测其
COM 表面,记录在 ../evidence/plant-sim-bridge/real-machine-probe/FINDINGS.md。
结论分两部分,必须一起读:
**已通的部分**:`Dispatch` 启动独立隐藏实例、`GetValue("Version")` →
`17000000`、`Quit` 干净退出、`LoadModel`/`SaveModel`、
`<class>.createObject(<frame>, x, y)` 创建对象、`NumNodes`/`node(i)` 枚举、
表格单元读写、`GetValue(<表达式>)` 回读、`EventController.reset/start/stop`
控制仿真。26 个探测全部 exit 0,单次 COM 调用最慢 0.289 s(多数 0.000 s),
整套约 36 s,`leaked_pids = []`(无残留进程)。
**后来打通的部分(关键是找到本机自带的帮助文件)**:本机安装目录里有
`Help/PlantSimulationENU.chm`(7-Zip 可解,13382 个 HTML),照着它改了两处:
* 连线是 `.MaterialFlow.Connector.connect(a, b)`——接收者是 Connector **类**,
不是对象、不是框架、也不是自由函数;
* MU 用 `MuSelection` + `Path` + `TimeOfGeneration` + `Interval` 配置
——Source 根本没有 `MU` / `Count` 这两个属性;
* 限定时长用 `EventController.End`(**相对时长**;`EndTime` 不存在),
并轮询 `isRunning` 等到结束——`start` 是异步的,睡一觉再读会读到移动靶。
实测:Source → SingleProc → Drain,1000 秒、10 秒投料、4 秒加工 → **100 个
工件**,`StatRelativeOccupation` = **0.4**,`StatWorkingTime` = **400 秒**;
连续三次运行**完全一致**。三个数字可以手算互证(1000/10=100 件;100×4=400 秒;
400/1000=0.4),所以这是可核验的闭环,不是"看起来跑起来了"。
**实测推翻了两个上游项目共同的三个假设**(这是本次最有价值的发现):
| 上游假设 | 实测结果 |
| --- | --- |
| 根框架是 `.Models.Model` | 是 `.Models.<模型名>.Frame`,模型名来自文件内容而非文件名;`NewModel()` 出来的框架**完全不可寻址** |
| `NumChildren` 判断创建成功 | 建了 3 个对象仍返回 `'0'`,必须用 `NumNodes` |
| 模型上建 `PsbridgeResult` 属性做两段式回读 | 属性不存在,赋值报"标识符未知";直接 `GetValue(<表达式>)` 即可 |
另有两条:信息流类必须带 `&` 前缀(`.InformationFlow.&TableFile`);
时间属性只能走 `ExecuteSimTalk` + 数字秒,`SetValue` 传字符串必失败。
第三条容易踩的坑:**SimTalk 的 `for` 循环用 `next` 关闭,不是 `end`**。
早期所有多行片段报"源代码包含语法错误"都是因为这个,而不是 `var` 声明
不可用;`print()` 的输出也读不回来,结构化结果只能写表格。
**边界(必须和结论一起说)**:2026-10-28 的初次单线闭环只是**固定投料节奏**;后续已在此基础上用 `scripts/real_machine_dispatch_smoke.py`(排程投料表 + Method 多机选路)和 `scripts/real_machine_jobshop_smoke.py`(2x2 交叉路线 + 缓冲防死锁)跑通真机原型。**当前仍未实机验证的范围**:同机多工序动态切换 `ProcTime` 的时间表达式、通用模板 `psbridge/simtalk/templates.py` 与 `Table_Ready` 决策闭环接线、以及含动态到达/柔性候选机的完整 DFJSP 调度器闭环——不要写成"通用 DFJSP 实验引擎已在实机全通"。
## 为什么需要这一层
对两个上游项目的源码审计结论(证据见 ../evidence/plant-sim-audit/,72 条主张
独立复核:68 CONFIRMED / 1 PARTIALLY_CONFIRMED / 3 NOT_VERIFIABLE_STATIC):
* Hsiang-Chin/plant-sim-mcp:有专用 COM 单线程执行器,但多步操作共享
ApiResult,缺少操作级原子性(api result 单槽位 + 14 处读取点,COM 层唯一锁
只护连接);create_device 会先删除同名对象;属性设置可能在不适用时静默跳过
却仍报告成功。仓库没有许可证文件,不能复制源码。
* Lang7910/PlantSimulation_mcp_skill:MIT 许可、45 个工具、有 Method/DataTable/
模型探索能力,但没有显式 STA 执行器、没有批量实验/随机种子/指标层;
save_model 指定新路径后未同步控制器路径;范围读取逐单元格调用 COM。
所以本项目的定位是:借鉴 A 的 COM 线程纪律与 B 的工具覆盖面,重建一个实验优先
的桥接层,并把"可复现、可核验"作为硬约束。审计发现到设计对策的逐条映射见
docs/AUDIT_TO_DESIGN.md。
## 架构
MCP / AI 客户端(python -m psbridge.mcp,stdio JSON-RPC 2.0)
|
psbridge.experiment 实例、批量运行、指标、有效性门禁、结果落盘
| (FakeDispatchEngine 或真实 SimTalk 引擎)
psbridge.scheduling FIFO/SPT/EDD 策略、独立参考 oracle、决策环境
|
psbridge.com 单线程 COM 网关、后端协议、Win32/Fake 后端
|
Tecnomatix Plant Simulation (COM + SimTalk)
核心设计约束:
1. **实验层与 COM 层解耦。** DispatchEngine 协议是唯一接缝,假引擎与未来的
SimTalk 引擎共享同一套语义(S1–S9),因此实验方法论可以在没有 Plant
Simulation 的机器上完整开发和验证。
2. **两个独立实现互相核验。** psbridge/scheduling/reference.py 是从书面规范独立
写出的 oracle,不读引擎实现;随机实例上两者必须给出完全相同的调度。
3. **失败绝不静默。** 无效调度、跨核验不一致、引擎异常分别记为 invalid / failed,
仍然落盘,但永远不会被计入有效结果。
4. **只改副本。** 实验在模型克隆上运行,原模型文件 sha256 前后比对,变化即失败。
## 快速开始
~~~bash
python -m pytest # 完整测试,无需 Plant Simulation
python -m psbridge.demo # FIFO/SPT/EDD 对比,多个随机种子
python -m psbridge.demo --json # 同上,输出 JSON
# MCP 传输层:python -m psbridge.mcp 启动 stdio 服务器
# 工具层(纯函数,无传输依赖)
python -c "from psbridge import tools; print(tools.tool_names())"
# 实机验收脚手架(默认 dry-run,只生成 SimTalk 源码与参考调度)
python scripts/real_machine_smoke.py --instance .psbridge-work/instance_demo.json --policy fifo
~~~
示例输出(demo 实例,5 工件 x 3 工序,4 台机器,3 个种子,5% 工期抖动):
policy runs valid invalid failed makespan(mean/min/max) tardiness(mean) tardy_jobs wip
edd 3 3 0 0 29.07/28.74/29.37 0.00 0.00 2.84
fifo 3 3 0 0 23.24/22.68/23.90 0.93 1.00 3.30
spt 3 3 0 0 23.99/23.85/24.22 0.78 1.00 3.23
这个结果本身不是研究结论(实例太小、策略太简单),它的作用是证明闭环可跑通、
指标可计算、记录可复现。
## 目录
* psbridge/schema.py 实例数据模型与严格校验(稳定契约)
* psbridge/errors.py 错误层级与错误码(稳定契约)
* psbridge/config.py 运行时配置(超时、路径白名单、后端选择)
* psbridge/experiment/ 引擎协议、批量 runner、指标、有效性检查、落盘
* psbridge/scheduling/ 派工策略、独立参考实现、RL 决策环境
* psbridge/com/ COM 网关与后端协议(Win32 / Fake)
* psbridge/core/ 经过验证的安全操作层
* psbridge/simtalk/ SimTalk 代码生成(部分假设已被实测推翻,见 docs/REAL_MACHINE_CHECKLIST.md §7)
* psbridge/tools.py MCP 就绪的工具层(30 个工具:9 个实验/离线工具 + 21 个模型/仿真/方法工具;模型会话走单 STA `ComExecutor`,无传输依赖)
* psbridge/mcp/ stdio JSON-RPC 2.0 传输层(python -m psbridge.mcp)
* psbridge/demo.py 闭环演示
* scripts/real_machine_smoke.py 实机验收脚手架(默认 dry-run)
* tests/ 无 Plant Simulation 也能运行的完整测试
* docs/ 架构、审计映射、实机验收清单
## 明确不在 v0.1 范围内
* ~~MCP 传输层~~ **已完成**:`psbridge/mcp/` 提供 stdio JSON-RPC 2.0 服务器,覆盖 initialize / tools/list / tools/call / ping / notifications。HTTP 传输未做。
* **多工序作业车间**:已跑通(`psbridge/simtalk/jobshop.py`,真机 smoke 七项校验全过)。关键是用**容量足够的工序间缓冲**吸收被阻塞的零件,机器一处理完立刻把件放走,因此不会出现「占着机器等下一台」的死锁。仍未覆盖:同一台机器上不同工序需要不同加工时间(需时间表达式,未验证);TableFile 投料表的列类型映射也仍未解。
* SimTalk 决策引擎的真实实现与 MU/Table_Ready 接线:依赖上一项。
* 柔性路由(多候选机器)、非单位机器容量:引擎对这些输入明确抛
EngineUnsupported,不会给出错误的答案。
* DFJSP(动态到达)、工人/运输资源耦合、强化学习训练:等闭环在实机通过后再扩展。
## 许可证与来源
本项目为 MIT。未从无许可证的 Hsiang-Chin/plant-sim-mcp 复制任何源码;
接口设计参考了 MIT 许可的 Lang7910/PlantSimulation_mcp_skill,详见
THIRD_PARTY_NOTICES.md。
TDQS
Scored across 30 tools
Most tools have clearly distinct resource+action targets, and potentially overlapping pairs (simulation_start vs simulation_run, run_experiment vs compare_policies vs schedule_vs_reference) are differentiated by their descriptions. A few boundaries in the experiment group remain fuzzy, e.g. run_experiment vs reproduce_run vs schedule_vs_reference all execute a policy over seeds.
Tools fall into clear families (model_*, simulation_*, method_*), but the pattern within families flips between noun_verb (model_status, simulation_status, experiment_summary) and verb_noun (model_open, simulation_start, validate_instance, list_runs). The unprefixed tools (bridge_info, generate_simtalk, object_set_control, mu_create_attribute) add further variation, though everything remains readable.
30 tools is heavy relative to the apparent scope, and several (model_get_property/model_object_info, model_status/bridge_info) could plausibly be consolidated. The surface feels like it grew by adding one tool per capability rather than being scoped deliberately.
Scheduling (validate/run/reproduce/compare/list/aggregate) and model manipulation cover strong lifecycle depth, but there are gaps: no way to close or save a model session, no method delete or method listing, and deletion is limited to devices via model_delete_device. Agents can work around these but will hit dead ends when cleaning up or discovering existing methods.