项目是面向设备交付、调试和验证工程师的单工程、单 PLC、多 HMI 页面和多控制逻辑 Qt 桌面编程器,提供 HMI 编辑、结构化梯形图、离线仿真和 Modbus RTU 真机联机。
本项目不生成、编译或下载 PLC 程序,不读取 PLC 内部程序与网络轨迹,不实现完整 XDPPro 指令集,也不支持任意自由画线。真机运行时 PLC 内部程序是唯一控制源,PC 只读写工程实际引用的 M/D 地址。
app/src/
├── domain/ 无 Qt 依赖的模型、值对象、校验和仓库接口
├── services/ 编辑、工程、仿真、运行模式和通信契约
├── infrastructure/ JSON、PLC 缓存、Qt Modbus RTU 实现
├── ui/ Qt Widgets 视图、对话框和界面控制器
└── main.cpp 对象创建与依赖注入
生产代码依赖方向固定为:
UI -> Services -> Domain
Infrastructure -> Services contracts + Domain
main.cpp -> UI + Services + Infrastructure
domain 不依赖 Qt、文件系统或串口services 执行业务用例和原子编辑,通过接口使用工程存储与 PLC 通信infrastructure 实现 JSON 工程存储、PLC 缓存和异步 Modbus RTUui 只提交命令并投影模型、轨迹和错误,不直接读写串口或承载业务校验main.cpp 只组合对象,不实现业务规则主窗口静态布局由 main_window.ui 管理。UI 协调分为:
| 组件 | 职责 |
|---|---|
ProjectWorkspaceController |
工程树、当前页面/逻辑会话选择和编辑对象刷新 |
PropertyPanelController |
HMI/梯形图选择、属性表单和编辑服务调用 |
RuntimePanelController |
运行会话、顶层运行监控窗口、运行值刷新和仿真故障反馈 |
RuntimeMonitorWindow |
独立顶层窗口的显示、隐藏、关闭请求和应用退出生命周期 |
RuntimeMonitorWidget |
全应用唯一运行投影,组合可交互 HMI、梯形图轨迹和自由监控 |
MainWindow |
窗口级动作、工程文件操作、控制器组合和模式切换 |
运行界面统一投影到独立顶层 RuntimeMonitorWindow 中的唯一 RuntimeMonitorWidget。该窗口是编程器的工程师运行监控大屏,不是旧的纯 HMI 操作员窗口:
ModePolicy 禁用编辑入口HmiNavigationService 和活动寄存器仓库完成;离线写虚拟 M/D,真机写 PLC,仿真停止或通信不可用时禁止写入MainWindow -> RuntimeModeService 返回编辑态。离线时先停止软件扫描,真机时保留 PLC 连接但撤销运行会话;应用退出时允许顶层窗口真正关闭Project
├── metadata
├── hmiPages[] + initialHmiPageId
├── alarmDefinitions[]
├── registerComments[]
└── controlLogics[]
└── rungs[]
├── condition
└── output
hmi_control_registry 统一描述dataPoints 表registerComments 只是 M/D 地址元数据,不保存当前值或引用位置,也不会让地址自动进入 PLC 轮询工程 JSON 格式固定为严格的 1.0。加载顺序为 JSON 解析、版本检查、字段解析、领域校验,全部成功后才能替换当前工程;保存使用原子提交。当前版本不读取旧 dataPoints、stages/branches 或缺少必填字段的同版本草稿。字段定义见 docs/工程格式说明.md。
校验分两级:
validate() 保证可保存结构合法,允许未绑定控件、待配置节点和未完成网络作为编辑草稿validateForRunning() 检查运行所需绑定和已启用逻辑;禁用草稿不阻止运行数量常量集中在 domain/project_limits.h,最终业务口径见 docs/用户使用/数量边界确认方案.md。领域、服务、JSON 和 UI 入口必须使用同一组常量,不能各自维护数字。
网络条件保存为规范化表达式树:
ConditionExpression
├── Node 触点、边沿或比较条件
├── Wire 无地址横线,保存条件区列跨度
├── Series 子表达式执行 AND
└── Parallel 子表达式执行 OR
输出是网络最右侧的单独节点。LogicEditorService 把串联、并联、旁路、替换和删除转换为表达式树操作,失败时回退整个编辑;LogicEditorWidget 只把树投影成正交网格。竖线由 Parallel 边界派生,网格坐标和自由线段不持久化,因此领域模型不会产生悬空线、环路或多输出路径。
单网络前 10 列为条件区,输出最多位于第 11 列;横线只能占条件区。第 11 个条件、过深表达式或过多并联行会在编辑和加载阶段被拒绝,具体数值以数量边界文档为准。
控制逻辑组可以暂时没有网络。新建工程和新建控制逻辑都保持 rungs=[],用户第一次添加条件、横线或输出时,编辑服务在同一个原子操作中创建网络;删除最后一个网络后也允许回到空逻辑。画布中的空网格只表示可编辑位置,不绘制真实横线;模型中的 Wire、实际节点、输出连接,以及并联短支路到右侧汇合点的必要结构连接,才投影为梯形图导线。并联短支路的结构连接由 UI 从 Parallel 宽度派生,不额外写入 Wire。连续追加单格横线占满十个条件列后,下一次无选中目标的追加会在同一撤销记录中创建下一个网络。
网络运行语义:
Wire 的逻辑值为真,但运行轨迹是否导通仍取决于上游电流节点配置使用 std::variant 的独立类型。当前条件包括 M 触点与上下沿、T/C 触点和 D 比较;输出包括普通/置位/复位 M 线圈、TON、CTU/CTD、MOVE、ADD/SUB。新增指令必须增加独立配置、校验、JSON、执行、编辑和测试分支,不能向通用节点堆叠无关字段。
SoftwareLogicExecutor 按 controlLogics 和网络数组顺序扫描已启用逻辑,前面网络的 M/D 写入在同一扫描周期对后面网络可见。T/C 是离线执行器内部资源,不映射 PLC T/C 区,也不进入 Modbus 轮询;计数器 CV/PV 等显式 M/D 引用仍使用统一仓库。扫描轨迹按逻辑 ID 分区,避免不同逻辑中的重复节点 ID 相互覆盖。
运行界面只访问 RegisterRepository。ActiveRegisterRepository 根据模式切换实际数据源:
编辑:UI -> Editor Services -> ProjectService -> Project
└── ProjectStorage -> JSON
离线:运行监控弹窗 -> ActiveRegisterRepository -> VirtualRegisterRepository
^
OfflineSimulationService(SoftwareLogicExecutor)
真机:运行监控弹窗 -> ActiveRegisterRepository -> PlcRegisterRepository(cache)
^
PLC <-> PlcCommunicationService <--------------------┘
| 模式 | 允许工程编辑 | 寄存器来源 | 软件逻辑执行器 | 进入条件 |
|---|---|---|---|---|
| 编辑态 | 是 | 无运行数据源 | 停止 | 运行态先退出 |
| 离线运行 | 否 | 虚拟 M/D | 运行 | 工程通过运行校验 |
| 真机运行 | 否 | PLC 读回缓存 | 停止 | PLC 已连接并完成本次首读 |
离线和真机不能直接互切。进入真机不复制或下发离线值,也不显示本地梯形图导通轨迹。
自由监控是会话级诊断工具,不属于 Project,不写入 JSON。它通过活动仓库读取和单点写入 M/D;真机写入仍必须经过连接与首读门槛。
PlcCommunicationService 使用 QModbusRtuSerialMaster 异步连接、轮询和单点写入,UI 线程不等待串口响应。Modbus 地址使用从 0 开始的原始地址。
轮询集合由 HMI、报警、梯形图中的显式 M/D 引用和自由监控地址合并,按区域去重并合并相邻地址。T/C 内部资源、横线和注释不进入轮询。动态修改监控地址时,当前请求按旧快照解析,空闲后再应用新集合。
PLC 缓存只保存最后一次成功读回值。写请求受理后不乐观修改缓存,界面等待后续轮询确认真实值。真机运行资格属于当前连接代次:必须完成完整首读,旧连接的异步回复不能污染重连后的状态。
通信状态包括 Disconnected / Connecting / Connected / Recovering / Faulted。连接失败或物理断线会关闭串口会话并进入 Disconnected;超时、协议或读写故障会停止正常轮询、保留最后有效缓存并进入 Faulted。所有通信故障都会撤销首读资格;若当时处于真机运行,运行模式服务立即返回编辑态。可恢复超时探测成功后进入 Recovering 并重新完整首读,但不会自动重新进入真机运行。
project_limits.h、数量边界文档和上下界测试hmi_control_registry,专用交互留在对应服务和图元