| @@ -121,6 +121,8 @@ HMI 控件不直接收发串口数据。通信服务更新联机寄存器缓存 | |||
| ## 6. 首次真机验证步骤 | |||
| 当前软件已实现串口参数配置、异步连接、地址收集、相邻地址合并轮询、PLC 缓存、单点写入、首次读取约束和错误反馈。自动化测试使用替身网关和缓存仓库验证,不等同于真实串口与 PLC 验证。 | |||
| 1. 用官方工具把 PLC 的 COM2 配置为 Modbus RTU 从站,设置并记录站号与串口参数。 | |||
| 2. 使用 USB 转 RS-485 将 PC 与 COM2 的 A/B 正确连接。 | |||
| 3. PC 发送 `0x01` 读取 `M0`,确认能得到合法 RTU 响应。 | |||
| @@ -1,139 +1,60 @@ | |||
| # Current Handoff | |||
| - Goal: 完成综合平台编程器的工程管理和后续编辑功能。 | |||
| - Branch: `main`。 | |||
| - Current status: 已完成开发顺序第 8 步,HMI、虚拟 M/D 和固定周期软件逻辑执行器的离线闭环已完成。 | |||
| - Changed files: | |||
| - `app/integrated_platform.pro` | |||
| - `app/src/main.cpp` | |||
| - `app/src/ui/main_window.h` | |||
| - `app/src/ui/main_window.cpp` | |||
| - `app/src/ui/main_window.ui` | |||
| - `AGENTS.md` | |||
| - `docs/C++代码规范.md` | |||
| - `docs/architecture.md` | |||
| - `app/src/domain/.gitkeep` | |||
| - `app/src/services/.gitkeep` | |||
| - `app/src/infrastructure/.gitkeep` | |||
| - `app/tests/.gitkeep` | |||
| - `scripts/build_and_run_qt.ps1` | |||
| - `app/src/domain/register_address.h` | |||
| - `app/src/domain/register_address.cpp` | |||
| - `app/src/domain/register_repository.h` | |||
| - `app/src/domain/register_repository.cpp` | |||
| - `app/src/domain/hmi_model.h` | |||
| - `app/src/domain/hmi_model.cpp` | |||
| - `app/src/domain/control_logic_model.h` | |||
| - `app/src/domain/control_logic_model.cpp` | |||
| - `app/src/domain/project_model.h` | |||
| - `app/src/domain/project_model.cpp` | |||
| - `app/src/domain/runtime_state.h` | |||
| - `app/src/domain/runtime_state.cpp` | |||
| - `app/src/domain/project_storage.h` | |||
| - `app/src/services/project_service.h` | |||
| - `app/src/services/project_service.cpp` | |||
| - `app/src/services/hmi_editor_service.h` | |||
| - `app/src/services/hmi_editor_service.cpp` | |||
| - `app/src/services/hmi_runtime_service.h` | |||
| - `app/src/services/hmi_runtime_service.cpp` | |||
| - `app/src/services/runtime_mode_service.h` | |||
| - `app/src/services/runtime_mode_service.cpp` | |||
| - `app/src/infrastructure/json_project_storage.h` | |||
| - `app/src/infrastructure/json_project_storage.cpp` | |||
| - `app/tests/domain_tests.pro` | |||
| - `app/tests/domain_tests.cpp` | |||
| - `app/tests/project_management_tests.pro` | |||
| - `app/tests/project_management_tests.cpp` | |||
| - `app/tests/hmi_editor_service_tests.pro` | |||
| - `app/tests/hmi_editor_service_tests.cpp` | |||
| - `app/tests/runtime_mode_service_tests.pro` | |||
| - `app/src/services/software_logic_executor.h` | |||
| - `app/src/services/software_logic_executor.cpp` | |||
| - `app/src/services/offline_simulation_service.h` | |||
| - `app/src/services/offline_simulation_service.cpp` | |||
| - `app/tests/offline_simulation_service_tests.pro` | |||
| - `app/tests/offline_simulation_service_tests.cpp` | |||
| - `app/tests/runtime_mode_service_tests.cpp` | |||
| - `app/tests/main_window_tests.pro` | |||
| - `app/tests/main_window_tests.cpp` | |||
| - `app/src/ui/hmi_editor_widget.h` | |||
| - `app/src/ui/hmi_editor_widget.cpp` | |||
| - `app/src/services/logic_editor_service.h` | |||
| - `app/src/services/logic_editor_service.cpp` | |||
| - `app/src/ui/logic_editor_widget.h` | |||
| - `app/src/ui/logic_editor_widget.cpp` | |||
| - `app/tests/logic_editor_service_tests.pro` | |||
| - `app/tests/logic_editor_service_tests.cpp` | |||
| - `app/tests/runtime_mode_service_tests.pro` | |||
| - Decisions made: | |||
| - Qt 应用源码位于 `app/`,构建输出位于被忽略的 `build/`。 | |||
| - 使用 qmake、Qt Widgets、Qt SerialBus 和 MinGW 8.1。 | |||
| - 主窗口只承载基础界面,不承担 PLC 通信或控制逻辑。 | |||
| - 简单函数、构造函数和析构函数不添加重复性注释。 | |||
| - 生产代码按 `UI -> Services -> Domain` 的方向依赖;基础设施实现领域接口。 | |||
| - 领域模型仅使用 C++17 标准库,不依赖 Qt UI、串口或 Modbus。 | |||
| - M 地址只承载布尔值,D 地址只承载带符号 16 位值,地址范围统一为 `0~4000`。 | |||
| - `RegisterRepository` 统一虚拟寄存器和真实 PLC 缓存的访问契约。 | |||
| - `ProjectStorage` 是领域层的工程文件存储契约,具体 JSON 和 Qt 文件 API 位于基础设施层。 | |||
| - `ProjectService` 负责工程路径、修改状态和新建保存加载流程;加载成功后才替换当前工程。 | |||
| - 工程文件格式版本当前为 `1.0`,保存使用 `QSaveFile` 原子提交。 | |||
| - 控制逻辑节点使用 `std::variant` 组合触点、线圈和数值比较的独立配置类型。 | |||
| - 当前控制逻辑范围不包含定时器;不得因为设备手册或旧开发计划提前加入未确认功能。 | |||
| - 新增逻辑节点时增加独立配置类型,不向通用节点结构堆叠无关可选字段。 | |||
| - 测试按业务风险分级 优先覆盖领域规则 服务闭环 工程存储和通信异常 不为简单访问器堆叠单测。 | |||
| - 运行态之间不可直接切换;真机运行态必须先完成 PLC 初次读取,且不运行软件逻辑执行器。 | |||
| - 主窗口采用工程树、中央编辑标签页、属性区、输出区和状态栏组成的可停靠工具布局。 | |||
| - 编辑、离线运行和真机运行共用一个互斥模式入口,切换规则由服务层调用领域状态机。 | |||
| - `RuntimeModeService` 直接复用领域模式、策略和结果类型,不维护一比一的服务层副本。 | |||
| - 运行态禁用工程树和属性编辑区,中央页面保持可见供后续 HMI 运行画面使用。 | |||
| - 当前尚未接入 PLC 通信,真机运行入口会按领域规则提示先连接并完成首次读取。 | |||
| - 项目代码不使用自定义命名空间,分层由目录、接口和依赖方向表达。 | |||
| - `HmiEditorService` 负责页面控件的添加、删除、移动和属性修改,画布项只保存控件标识。 | |||
| - 控件拖动必须限制在页面内,控件标识在页面内唯一;未绑定控件可作为草稿保存,但不得进入离线运行。 | |||
| - 新增控件先以未绑定状态进入编辑器,避免自动分配地址造成误绑定。 | |||
| - 离线运行的按钮采用点击切换 M 位语义,数值输入写入一个带符号 16 位 D 字。 | |||
| - `HmiRuntimeService` 仅通过 `RegisterRepository` 访问寄存器,不依赖虚拟寄存器、串口或 Modbus。 | |||
| - 控制逻辑使用梯形图层级:逻辑包含网络,网络包含串联条件级,条件级内部包含并联条件,输出线圈固定在右侧。 | |||
| - 条件级之间执行 AND,同级并联条件执行 OR;状态保持由常闭停止与“常开启动 OR 常开运行反馈”串联后驱动运行线圈。 | |||
| - `LogicEditorService` 负责网络、串联条件、并联条件、输出线圈、属性和删除操作。 | |||
| - `LogicEditorWidget` 自动布局母线、支路和节点,不允许自由拖动或手动画线,不保存节点坐标。 | |||
| - 控制逻辑编辑器不访问 HMI、寄存器仓库或 PLC,工程格式版本保持 `1.0`。 | |||
| - 新增 HMI 控件立即进入工程内存但默认未绑定,属性区明确显示“未绑定”,不再以 `M0/D0` 伪装默认绑定。 | |||
| - 新增梯形图节点立即进入工程内存,但 `configured=false`;画布显示 `< M 地址 >`、`< D 地址 >` 或 `< 常量 >`,点击应用逻辑属性后才成为已配置节点。 | |||
| - `Project::validate()` 是草稿保存和加载校验,允许未绑定 HMI、待配置节点及未完成网络;`Project::validateForRunning()` 额外校验可运行完整性。 | |||
| - `RuntimeModeService` 依赖 `ProjectService` 的只读工程,在进入离线运行态前执行完整性校验,拒绝未配置或未完成的工程。 | |||
| - JSON 节点新增 `configured` 字段;缺失字段的旧版 `1.0` 工程按历史逻辑视为已配置,保持兼容读取。 | |||
| - 梯形图 UI 已按 LAD 可读性优化:普通线圈采用标准 `( )` 形状,导线连接元件端子而不穿透符号,比较条件显示为带数据类型和上下操作数的指令块。 | |||
| - 软件逻辑执行器按逻辑、网络顺序扫描,级内 OR、级间 AND,前面网络写入对后面网络同周期可见。 | |||
| - 普通线圈每周期写结果,置位/复位线圈只在网络成立时写入;同一地址混用不同线圈模式会在启动前被拒绝。 | |||
| - 离线仿真以 50 ms 为目标扫描周期,启动时复制逻辑快照并清空虚拟 M/D,退出时先停止执行器再返回编辑态。 | |||
| - 仿真状态使用 `Stopped`、`Running`、`Faulted`,记录扫描次数和逻辑/网络/节点错误上下文;故障后 HMI 保持最终值可见但禁止写入。 | |||
| - Validation run and results: | |||
| - 在 `build/baseline/` 执行 qmake 与 `mingw32-make -j2`,构建成功。 | |||
| - 已启动 `integrated_platform.exe` 并正常退出。 | |||
| - 已通过 `scripts/build_and_run_qt.ps1` 完成 Debug 构建和启动验证。 | |||
| - 新增分层目录后再次完成 Debug 构建和启动验证。 | |||
| - 已确认构建目录、临时目录和本地草稿文档被 `.gitignore` 忽略。 | |||
| - 在 `build/core-model/` 重新执行 qmake 和 `mingw32-make -j2`,应用构建成功。 | |||
| - 在 `build/domain-tests/` 构建并运行领域测试,输出 `domain tests passed`。 | |||
| - 控制逻辑节点配置重构后再次构建应用和领域测试,均通过。 | |||
| - 在 `build/project-management-tests/` 构建并运行工程管理测试,输出 `project management tests passed`。 | |||
| - 工程管理加入应用后重新执行 Qt 应用构建,构建成功。 | |||
| - 在 `build/main-window/` 构建主窗口和运行模式服务,Qt 5.15.2 Debug 构建成功。 | |||
| - 运行模式服务测试输出 `runtime mode service tests passed`。 | |||
| - 主窗口模式工作流测试输出 `main window tests passed`。 | |||
| - 使用当前工作树重新构建并运行领域测试,输出 `domain tests passed`。 | |||
| - 启动 Debug 应用完成窗口响应冒烟检查,程序正常启动和退出。 | |||
| - 移除自定义命名空间后,Release 应用及五个测试工程构建成功,全部测试通过。 | |||
| - 在 `build/hmi-editor/` 重新构建 Release 应用,构建成功并完成启动冒烟检查。 | |||
| - 在 `build/hmi-editor-service-tests/` 构建并运行 HMI 服务测试,输出 `HMI editor service tests passed`。 | |||
| - 重新构建并运行领域、工程管理和主窗口测试,全部通过。 | |||
| - 控制逻辑编辑器完成后,领域、逻辑编辑服务、工程管理和主窗口测试全部通过。 | |||
| - Debug 应用构建成功。 | |||
| - 启动 Debug 应用确认主窗口创建成功,窗口标题为“综合平台编程器”。 | |||
| - 梯形图结构重构后,领域、逻辑编辑服务、工程管理和主窗口测试重新构建并全部通过。 | |||
| - 待配置节点改造后,Release 应用构建成功;领域、工程管理、逻辑编辑服务、运行模式和主窗口测试全部通过。 | |||
| - 离线仿真实现后,Release 应用构建成功;离线仿真、领域、工程管理、HMI 编辑、逻辑编辑、运行模式和主窗口测试全部通过。 | |||
| - Remaining work: 进入开发顺序第 9 步,实现 Modbus RTU 真机通信。 | |||
| - Known risks / blockers: 无。 | |||
| - Suggested next command: 建立异步 Modbus RTU 通信服务,先完成串口配置、连接和 M0/D0 安全读写。 | |||
| - Goal: 完成综合平台编程器的结构化梯形图、数据点管理、离线仿真和 Modbus RTU 真机链路 | |||
| - Branch: `main` | |||
| - Current status: 第 1~10 步的软件实现与自动化测试已完成,待连接 XDH-60T4-E 进行真机安全验证和整理最终交付资料 | |||
| ## 当前能力 | |||
| - 梯形图条件使用递归 `Node / Series / Parallel` 表达式树,可表示 `A OR (B AND C)` 和嵌套串并联 | |||
| - 编辑器支持节点前后串联、节点并联、整支路并联、删除节点或支路、设置输出线圈,并按结构自动布线 | |||
| - 执行器递归求值并生成节点、表达式、网络轨迹,离线运行时画布显示导通路径和故障位置 | |||
| - 数据点表统一维护地址、类型、名称、注释、当前值和 HMI/LAD 引用位置 | |||
| - `ActiveRegisterRepository` 在离线虚拟仓库与 PLC 缓存仓库之间切换 | |||
| - `PlcCommunicationService` 使用 Qt SerialBus 异步连接、轮询和单点写入,UI 不阻塞 | |||
| - 真机连接收集数据点、HMI 和梯形图地址,按 M/D 分区并合并相邻地址读取 | |||
| - 真机模式要求首次读取完成,不复制离线值,不运行 PC 软件逻辑执行器 | |||
| - 通信错误停止轮询并保留最后一次有效缓存,状态栏和日志显示错误 | |||
| - 主窗口静态控件已迁入 `main_window.ui`,PLC 参数由独立配置对话框收集后再发起连接 | |||
| ## 关键决策 | |||
| - 工程格式继续使用 `1.0`,当前文件直接保存表达式树和数据点 | |||
| - 不读取旧的 `stages/branches` 结构,不添加旧格式迁移和兼容层 | |||
| - 梯形图编辑结构而不是自由线段,避免悬空线、环路和多个输出路径 | |||
| - 条件节点继续使用 `std::variant` 的独立配置类型,不向通用节点添加无关字段 | |||
| - HMI、梯形图和通信共享 `M0~M4000`、`D0~D4000` 数据点范围 | |||
| - PLC 读写使用从 `0` 开始的原始 Modbus 地址 | |||
| - HMI 写请求不乐观修改 PLC 缓存,由下一次轮询确认真实值 | |||
| - 真机运行时 PLC 内部程序是控制源,软件逻辑执行器保持停止 | |||
| ## 自动化验证 | |||
| 以下测试均已完成干净构建并通过: | |||
| - `domain_tests` | |||
| - `logic_editor_service_tests` | |||
| - `offline_simulation_service_tests` | |||
| - `project_management_tests` | |||
| - `hmi_editor_service_tests` | |||
| - `runtime_mode_service_tests` | |||
| - `data_point_service_tests` | |||
| - `plc_runtime_tests` | |||
| - `main_window_tests` | |||
| `main_window_tests` 还覆盖 PLC 配置参数往返、独立配置弹窗入口,以及主窗口不再内嵌串口参数控件。 | |||
| Qt Release 应用已完成干净构建,并执行了启动与正常退出冒烟验证。`git diff --check` 无空白错误。 | |||
| ## 待完成事项 | |||
| 1. 在 PLC STOP 状态确认 COM2 参数和 RS-485 A/B 接线 | |||
| 2. 用安全测试地址读取 `M0`、`D0` | |||
| 3. 写入 `M0`、`D0` 并由 PLC 监控和下一次轮询确认 | |||
| 4. 验证断线、超时和重新连接流程 | |||
| 5. PLC 切换 RUN 后执行完整 HMI 与设备联动测试 | |||
| 6. 整理测试记录、构建说明和演示工程 | |||
| ## 建议下一步 | |||
| 运行 Release 应用,配置实际 COM 口、站号、波特率和校验方式,在 PLC STOP 状态完成 `M0`、`D0` 首次安全读写。未确认设备程序与负载安全前不要在 RUN 状态写控制地址。 | |||
| @@ -1,140 +1,136 @@ | |||
| # 综合平台编程器架构 | |||
| ## 目标 | |||
| ## 目标与边界 | |||
| 项目采用 UI、领域、服务和基础设施分层。HMI 编辑、离线仿真和真机联机使用同一套领域模型,UI 不直接依赖串口或 Modbus 实现。 | |||
| 项目采用 UI、领域、服务和基础设施分层。HMI 编辑、结构化梯形图、离线仿真和真机联机共用工程模型,UI 不直接依赖文件系统、串口或 Modbus 实现。 | |||
| 项目代码不使用自定义命名空间,分层边界由目录、接口和依赖方向表达。 | |||
| 当前目标是完成基础设备控制闭环,不生成、编译或下载 PLC 程序,不实现 XDPPro 的完整指令集,也不提供任意自由画线。 | |||
| ## 目录职责 | |||
| ## 目录与依赖 | |||
| ```text | |||
| app/ | |||
| ├── integrated_platform.pro qmake 工程入口 | |||
| ├── src/ | |||
| │ ├── main.cpp 应用组合入口 | |||
| │ ├── ui/ Qt Designer 界面、视图和交互协调 | |||
| │ ├── domain/ 工程、HMI、逻辑、M/D 地址等纯业务模型 | |||
| │ ├── services/ 用例编排、运行模式和仿真服务 | |||
| │ └── infrastructure/ 工程文件、Modbus RTU 等外部实现 | |||
| │ ├── main.cpp 对象创建与依赖注入 | |||
| │ ├── ui/ Qt Widgets 视图和交互协调 | |||
| │ ├── domain/ 工程、HMI、梯形图、数据点和寄存器模型 | |||
| │ ├── services/ 编辑、仿真、运行模式和通信契约 | |||
| │ └── infrastructure/ JSON、PLC 缓存和 Qt Modbus RTU 实现 | |||
| └── tests/ 单元测试与集成测试工程 | |||
| ``` | |||
| ## 依赖方向 | |||
| 生产代码必须保持单向依赖: | |||
| 生产代码保持以下依赖方向: | |||
| ```text | |||
| UI -> Services -> Domain | |||
| Infrastructure -> Domain | |||
| Infrastructure -> Services contracts + Domain | |||
| main.cpp -> UI + Services + Infrastructure | |||
| ``` | |||
| - `domain` 不依赖 UI、串口、文件系统或具体 PLC 库。 | |||
| - `services` 只通过领域定义的接口使用寄存器、工程存储等能力。 | |||
| - `infrastructure` 实现外部能力,例如 JSON 工程文件和 Qt Modbus RTU。 | |||
| - `ui` 只调用服务并展示状态;`MainWindow` 只组织界面,不实现业务规则、寄存器读写或通信流程。 | |||
| - `main.cpp` 负责创建对象并注入依赖,不承载业务逻辑。 | |||
| - `domain` 不依赖 Qt、串口和文件系统 | |||
| - `services` 编排用例,通过接口使用工程存储、寄存器和 PLC 通信 | |||
| - `infrastructure` 实现 JSON 工程文件、PLC 缓存和异步 Modbus RTU | |||
| - `ui` 只提交操作并展示模型、运行轨迹和错误状态 | |||
| - `main.cpp` 负责组合依赖,不承载业务规则 | |||
| 主窗口的菜单、工具栏、属性表单和数据点面板由 Qt Designer 的 | |||
| `main_window.ui` 定义。C++ 仅创建 HMI/梯形图自定义画布、填充运行时数据并连接服务层信号。 | |||
| 真实 PLC 的串口与轮询参数由独立的 `PlcConnectionDialog` 收集,主窗口只保留配置连接和断开入口。 | |||
| ## 核心领域模型 | |||
| | 文件 | 职责 | | |||
| | --- | --- | | |||
| | `register_address.*` | 定义 `M0~M4000`、`D0~D4000` 地址和值域规则 | | |||
| | `register_repository.*` | 定义 M/D 统一读写接口和离线内存实现 | | |||
| | `active_register_repository.*` | 在虚拟仓库与 PLC 缓存仓库之间切换当前数据源 | | |||
| | `data_point_model.*` | 定义地址、名称和注释统一数据点 | | |||
| | `hmi_model.*` | 定义页面、控件、布局和寄存器绑定 | | |||
| | `control_logic_model.*` | 定义梯形图表达式树、条件节点和输出线圈 | | |||
| | `project_model.*` | 聚合数据点、HMI 页面和控制逻辑并执行工程校验 | | |||
| | `runtime_state.*` | 定义编辑、离线运行和真机运行状态机 | | |||
| 工程文件格式保持 `1.0`。当前版本直接保存表达式树和数据点,不读取旧的 `stages/branches` 结构,也不包含旧格式迁移代码。加载必须先完成 JSON 解析、版本检查和领域校验,成功后才能替换当前工程;保存使用原子提交。 | |||
| ## 核心数据流 | |||
| `Project::validate()` 允许未绑定控件、待配置节点和未完成网络作为编辑草稿保存。`Project::validateForRunning()` 额外拒绝未绑定 HMI、待配置节点、空条件和缺少输出的网络。 | |||
| ## 结构化梯形图 | |||
| 网络条件采用递归表达式: | |||
| ```text | |||
| 用户操作 HMI | |||
| -> UI 发起服务请求 | |||
| -> 服务校验并更新领域模型或寄存器接口 | |||
| -> 仿真服务 / 真机 Modbus 实现执行 | |||
| -> 服务发布状态变化 | |||
| -> UI 刷新控件和状态栏 | |||
| ConditionExpression | |||
| ├── Node 单个触点或比较条件 | |||
| ├── Series 子表达式依次串联,执行 AND | |||
| └── Parallel 子表达式并联,执行 OR | |||
| ``` | |||
| 离线模式由服务连接虚拟 M/D 寄存器与软件逻辑执行器;真机模式由服务连接 PLC 寄存器缓存与 Modbus RTU 服务。两种模式共用 HMI 绑定模型,且软件逻辑执行器不得在真机模式运行。 | |||
| 例如 `A OR (B AND C)` 表达为: | |||
| ## 核心领域模型 | |||
| ```text | |||
| Parallel | |||
| ├── Node(A) | |||
| └── Series | |||
| ├── Node(B) | |||
| └── Node(C) | |||
| ``` | |||
| 领域模型位于 `app/src/domain/`,只使用 C++17 标准库,不包含 Qt 界面、串口或 Modbus | |||
| 类型: | |||
| `LogicEditorService` 支持在节点前后串联插入、与节点并联、与整个支路并联、删除节点或支路,以及设置或替换输出线圈。删除后会归一化空容器和只有一个子项的容器,防止无效嵌套进入执行器。 | |||
| | 文件 | 职责 | | |||
| | --- | --- | | |||
| | `register_address.*` | 定义 M/D 地址值对象,统一校验项目地址范围 `0~4000`。 | | |||
| | `register_repository.*` | 定义 M 位和 D 字的统一读写接口,并提供离线内存实现。 | | |||
| | `project_storage.*` | 定义工程文件保存和加载的领域存储契约,不依赖具体文件格式。 | | |||
| | `hmi_model.*` | 定义 HMI 页面、控件、位置、扩展属性和寄存器绑定,并校验控件位于页面边界内。 | | |||
| | `control_logic_model.*` | 定义网络、串联级、并联条件和输出线圈,并校验节点类型及地址区域。 | | |||
| | `project_model.*` | 聚合工程元数据、HMI 页面和控制逻辑,并校验工程内标识唯一性。 | | |||
| | `runtime_state.*` | 定义编辑态、离线运行态、真机运行态及合法切换规则。 | | |||
| 工程管理由 `services/ProjectService` 编排新建、保存、另存为和加载。JSON 文件转换和原子写入 | |||
| 由 `infrastructure/JsonProjectStorage` 实现。加载必须先完成文件解析、版本检查和领域校验,成功 | |||
| 后才能替换当前工程;保存失败或加载失败不得修改当前工程路径和内存内容。 | |||
| 工程编辑支持保留草稿:HMI 非标签控件可以暂时没有寄存器绑定,梯形图节点可以处于待配置状态, | |||
| 这些状态仍通过 `Project::validate()` 保存和加载。进入离线运行前由 | |||
| `Project::validateForRunning()` 额外拒绝未绑定控件、待配置节点和缺少条件/输出的网络;旧版 1.0 | |||
| 工程缺少节点 `configured` 字段时按历史完整节点兼容读取。 | |||
| `services/HmiEditorService` 负责 HMI 页面的控件添加、删除、移动和属性更新,不依赖 Qt 图元。 | |||
| `ui/HmiEditorWidget` 使用 `QGraphicsScene` 将模型投影为画布项,画布项只保存控件标识,移动结束后 | |||
| 才通过编辑服务提交新位置。草稿保存时 `Project::validate()` 只拒绝已填写但区域错误或非法的绑定; | |||
| 进入离线运行前 `Project::validateForRunning()` 要求必需绑定完整。 | |||
| `RegisterRepository` 是 HMI、逻辑执行器和运行服务唯一可见的寄存器边界。离线运行使用 | |||
| `VirtualRegisterRepository`;真机运行后续注入由基础设施层实现的 PLC 缓存仓库。仓库读取的是 | |||
| 最近一次有效值,写入成功只表示请求被仓库接受;真实 PLC 的异步确认由服务层负责反馈。 | |||
| `services/HmiRuntimeService` 仅持有 `RegisterRepository` 接口:按钮切换 M 位,指示灯读取 M 位, | |||
| 数值显示读取 D 字,数值输入写入 D 字。它不依赖虚拟寄存器实现、串口或 Modbus,因此离线和真机 | |||
| 运行可替换数据源而无需改变 HMI 模型或画布代码。 | |||
| `services/SoftwareLogicExecutor` 负责一次确定性梯形图扫描。执行顺序为控制逻辑、网络、串联级, | |||
| 同级并联条件执行 OR,不同串联级执行 AND;普通线圈每周期写入网络结果,置位和复位线圈只在 | |||
| 网络成立时写入。前面网络的写入对后面网络在同一扫描周期立即可见。启动前拒绝同一 M 地址混用 | |||
| 不同线圈模式,避免运行结果依赖隐藏顺序。 | |||
| `services/OfflineSimulationService` 使用 `Qt::PreciseTimer` 以 50 ms 目标周期驱动扫描器。服务启动时 | |||
| 先校验并复制控制逻辑快照,再清空虚拟 M/D 并开始扫描;退出离线态时先停止扫描再开放工程编辑。 | |||
| 该周期不提供硬实时保证。服务记录 `Stopped`、`Running`、`Faulted` 实际状态、成功扫描次数和最近 | |||
| 错误上下文。发生仓库读写或执行错误时停止定时器、保留最终寄存器值和逻辑/网络/节点定位;HMI | |||
| 仍可读取最终值,但按钮和数值输入进入只读状态。 | |||
| 控制逻辑节点使用 `std::variant` 组合独立配置类型。初版只包含 M 区触点、M 区线圈和 D 值与 | |||
| 常量比较。新增节点时应增加独立配置类型及其校验和执行处理,不得向通用 `LogicNode` 持续 | |||
| 添加只对单一节点有效的可选字段。定时器不是当前原始需求范围,不提前建立模型或执行逻辑。 | |||
| 控制逻辑按标准梯形图的受限结构表达:一个逻辑包含多个网络,一个网络包含按顺序串联的多个 | |||
| 条件级,每个条件级包含一个或多个并联条件,输出线圈固定在网络最右侧。条件级之间执行逻辑与, | |||
| 同级并联条件执行逻辑或。状态保持通过启动触点和运行反馈触点并联,并与停止触点串联后驱动运行 | |||
| 线圈表达。该结构是后续软件逻辑执行器的确定性求值契约,不需要通用图遍历或连接表。 | |||
| `services/LogicEditorService` 负责新增和删除网络、追加串联条件、增加并联条件、设置输出线圈、 | |||
| 修改属性和删除节点。空网络和未完成网络均可作为编辑草稿保存;进入离线运行前,非空网络必须同时 | |||
| 具备条件和输出线圈,且所有节点均已配置。 | |||
| `ui/LogicEditorWidget` 自动排列左右母线、串联级、并联支路和右侧线圈,不允许自由拖动或手动画线, | |||
| 也不保存像素坐标。编辑器通过服务提交修改,不访问 HMI、寄存器仓库或 PLC。 | |||
| `LogicEditorWidget` 根据表达式树计算布局并自动生成横线、竖线和支路汇合线。画布可选择节点或表达式范围,并显示选中范围和插入位置;用户编辑结构而不是直接保存线段,因此不会产生悬空线、环路和多输出路径。 | |||
| 条件节点使用 `std::variant` 保存独立配置类型,当前包含 M 常开/常闭触点和 D 值与常量比较。输出支持普通、置位和复位 M 线圈。新增节点类型时应增加独立配置类型及校验、存储和执行分支,不能向通用节点堆叠无关字段。 | |||
| ## 执行与运行反馈 | |||
| `SoftwareLogicExecutor` 按控制逻辑、网络顺序扫描,并递归求值 `Node / Series / Parallel`。普通线圈每周期写入网络结果,置位和复位线圈只在网络成立时写入;前面网络的写入对后面网络同一扫描周期立即可见。 | |||
| 每次扫描生成 `LogicTraceSnapshot`,记录节点、表达式、网络和线圈的导通状态。逻辑画布在运行时以绿色显示导通节点和路径;执行错误携带逻辑、网络和节点上下文,画布选择并以红色突出故障位置。 | |||
| `OfflineSimulationService` 以 50 ms 目标周期驱动扫描,启动时复制逻辑快照并清空虚拟 M/D。故障后停止扫描并保留最终寄存器值,HMI 可继续显示但禁止写入。该周期不提供硬实时保证。 | |||
| ## 数据点管理 | |||
| `DataPointService` 维护统一数据点表: | |||
| ```text | |||
| 地址 | 类型 | 名称 | 注释 | 当前值 | 引用位置 | |||
| ``` | |||
| 地址和名称在工程内唯一。引用位置从 HMI 绑定和梯形图节点动态计算;仍被引用的数据点不能删除或修改地址,但可以修改名称和注释。HMI 和梯形图属性区从同一数据点集合选择地址,运行时当前值由活动寄存器仓库读取。 | |||
| ## 寄存器与 Modbus RTU | |||
| `HmiRuntimeService` 和运行界面只访问 `RegisterRepository`。`ActiveRegisterRepository` 在两种数据源之间切换: | |||
| - 离线运行使用 `VirtualRegisterRepository` | |||
| - 真机运行使用 `PlcRegisterRepository` 的最近一次有效缓存 | |||
| `PlcCommunicationService` 使用 `QModbusRtuSerialMaster` 异步连接、读取和写入。连接时从数据点、HMI 和梯形图收集 M/D 地址,去重后按区域合并相邻地址进行轮询。M 使用线圈,D 使用保持寄存器;所有 Modbus 地址均为从 `0` 开始的原始地址。 | |||
| HMI 写入只表示异步请求已受理,不乐观修改 PLC 缓存,实际值由后续读回确认。超时、协议错误或断线会停止轮询、保留最后一次有效值并进入故障状态,错误通过运行模式服务交给 UI 展示。 | |||
| ## 运行模式边界 | |||
| | 模式 | 允许编辑工程 | 寄存器来源 | 软件逻辑执行器 | 进入约束 | | |||
| | 模式 | 允许编辑 | 寄存器来源 | 软件逻辑执行器 | 进入约束 | | |||
| | --- | --- | --- | --- | --- | | |||
| | 编辑态 | 是 | 无运行仓库 | 停止 | 运行态必须先回到编辑态。 | | |||
| | 离线运行态 | 否 | 虚拟 M/D | 运行 | 仅可从编辑态进入。 | | |||
| | 真机运行态 | 否 | PLC 读回缓存 | 停止 | 仅可从编辑态进入,且必须先完成 PLC 初次读取。 | | |||
| | 编辑态 | 是 | 无运行数据源 | 停止 | 运行态必须先退出 | | |||
| | 离线运行 | 否 | 虚拟 M/D | 运行 | 工程通过运行校验 | | |||
| | 真机运行 | 否 | PLC 读回缓存 | 停止 | PLC 已连接且首次读取完成 | | |||
| 离线运行态与真机运行态不得直接互切。进入真机运行态时不复制或写入离线仓库值,避免误写 | |||
| PLC;退出任一运行态后才能重新编辑工程。 | |||
| 离线运行与真机运行不得直接互切。进入真机模式不复制或下发离线值;PLC 内部程序是控制源,PC 端软件逻辑执行器必须停止。 | |||
| ## 测试边界 | |||
| 测试按业务风险和模块边界安排,不要求为每个简单访问器或转发函数单独编写测试: | |||
| | 优先级 | 测试对象 | 重点验证内容 | | |||
| | 优先级 | 测试对象 | 重点内容 | | |||
| | --- | --- | --- | | |||
| | 高 | `domain` | 地址边界、M/D 类型约束、模型校验、工程唯一性和运行模式切换 | | |||
| | 高 | `services` | HMI、寄存器仓库和逻辑执行器之间的离线闭环 | | |||
| | 高 | 工程存储 | 保存加载往返、版本字段、缺失字段和非法文件 | | |||
| | 中 | `infrastructure` | Modbus 帧解析、超时、CRC、断线和错误反馈 | | |||
| | 中 | `ui` | 新建工程、编辑、运行切换和错误提示等关键工作流 | | |||
| | 冒烟 | 真机联调 | 少量安全地址的读写和 RUN 联动验证 | | |||
| `domain` 和 `services` 的自动化测试不依赖 Qt 界面或真实 PLC。基础设施优先使用模拟串口 | |||
| 和测试文件,真机测试只覆盖无法在模拟环境确认的通信和联动行为。 | |||
| | 高 | 领域模型 | 地址边界、表达式校验与归一化、工程唯一性 | | |||
| | 高 | 编辑服务 | 嵌套串并联插入、删除、数据点引用保护 | | |||
| | 高 | 执行与模式服务 | 递归求值、轨迹、离线闭环、仓库切换和首次读取约束 | | |||
| | 高 | 工程存储 | `1.0` 往返、缺失字段、非法结构和不支持版本 | | |||
| | 中 | PLC 通信 | 缓存有效性、地址集合、异步读写和错误反馈 | | |||
| | 中 | UI | 编辑、运行切换、选择和错误定位工作流 | | |||
| | 冒烟 | 真机 | STOP 状态安全读写 M0/D0,随后 RUN 联动 | | |||
| 自动化测试不依赖真实 PLC。串口接线、PLC 参数和设备 RUN 行为只能通过现场真机测试确认。 | |||
| @@ -2,6 +2,10 @@ | |||
| > 本文规定项目的推荐开发顺序。原则是先建立稳定的数据模型和模块边界,再完成编辑功能,最后接入仿真与真实 PLC。每个阶段完成后都应保证项目可以构建、运行并提交一次 Git 记录。 | |||
| ## 当前进度 | |||
| 第 1~10 步的软件实现已完成,工程格式继续使用 `1.0`,不保留旧梯形图结构的兼容代码。当前重点是第 11 步中的真机安全验证和第 12 步交付资料。 | |||
| ## 1. 建立开发基线 | |||
| - 初始化 Git 仓库,确认 `.gitignore`、编码规则和提交规范。 | |||
| @@ -20,7 +24,7 @@ | |||
| ## 3. 定义核心数据模型与接口 | |||
| - 定义工程、HMI 页面、HMI 控件、控制逻辑和 M/D 地址模型。 | |||
| - 定义工程、HMI 页面、HMI 控件、结构化控制逻辑、统一数据点和 M/D 地址模型。 | |||
| - 定义统一的寄存器仓库接口,隔离虚拟寄存器与真实 PLC 寄存器。 | |||
| - 定义编辑态、离线运行态和真机运行态的状态边界。 | |||
| @@ -53,20 +57,30 @@ | |||
| ## 7. 实现控制逻辑编辑器 | |||
| - 建立梯形图逻辑模型,支持网络、串联条件、并联支路、输出线圈、删除和属性配置。 | |||
| - 使用 `Node / Series / Parallel` 表达式树建立梯形图逻辑模型,支持嵌套串并联关系。 | |||
| - 支持节点前后串联插入、与节点并联、与整个支路并联、删除和输出线圈配置。 | |||
| - 初版实现常开、常闭、普通线圈、置位/复位以及 D 值与常量比较。 | |||
| - 定时器不属于当前原始需求范围,只有在后续需求明确时才新增对应配置和执行逻辑。 | |||
| - 新增节点类型时使用独立配置类型,不向通用节点结构持续堆叠无关字段。 | |||
| - 编辑器只生成逻辑模型,不直接修改 HMI 或 PLC。 | |||
| - 画布按梯形图结构自动布局,不允许节点自由拖动,也不要求用户手动画线。 | |||
| - 画布按表达式结构自动布线,显示节点或支路选择范围,不允许节点自由拖动,也不保存自由线段。 | |||
| 完成标准:能够表达 `A OR (B AND C)` 等嵌套逻辑,并配置启动、停止和状态保持逻辑。 | |||
| 完成标准:能够配置一套简单的启动、停止和状态保持逻辑。 | |||
| ## 7.1 建立统一数据点表 | |||
| - 统一维护地址、类型、名称、注释、当前值和引用位置。 | |||
| - HMI 与梯形图从同一数据点集合选择 M/D 地址。 | |||
| - 防止删除或修改仍被引用的数据点地址。 | |||
| 完成标准:地址只需定义一次,能够查看 HMI 和梯形图中的全部引用位置。 | |||
| ## 8. 实现离线仿真运行 | |||
| - 实现虚拟 M/D 寄存器和固定周期的软件逻辑执行器。 | |||
| - 将 HMI、虚拟寄存器和控制逻辑形成完整闭环。 | |||
| - 实现启动、停止、异常和当前运行状态反馈。 | |||
| - 记录节点、支路和网络导通轨迹,在画布突出导通路径和故障节点。 | |||
| 完成标准:不连接 PLC 时,可以通过 HMI 操作并观察逻辑运行结果。 | |||
| @@ -75,8 +89,10 @@ | |||
| - 使用 Qt SerialBus 的 `QModbusRtuSerialMaster` 封装独立通信服务。 | |||
| - 实现串口配置、连接、断开、M/D 读写、轮询、超时和错误反馈。 | |||
| - 使用从 `0` 开始的原始 Modbus 地址,通信过程不得阻塞 UI。 | |||
| - 收集数据点、HMI 和梯形图地址,按 M/D 分区并合并相邻地址轮询。 | |||
| - 写请求不乐观更新缓存,等待下一次读回确认 PLC 实际值。 | |||
| 完成标准:先在 PLC STOP 状态下稳定读取和写入 `M0`、`D0`,再扩展到批量轮询。 | |||
| 完成标准:软件链路和自动化测试完成后,先在 PLC STOP 状态下稳定读取和写入 `M0`、`D0`,再验证批量轮询。 | |||
| ## 10. 完成运行模式集成 | |||
| @@ -96,6 +112,8 @@ | |||
| 完成标准:核心流程可重复执行,异常有反馈,程序不会崩溃或误写 PLC。 | |||
| 当前待办:自动化回归已覆盖核心领域、编辑、存储、仿真、运行模式、PLC 缓存和主界面;真实串口、接线、STOP 安全写入和 RUN 联动仍需连接 XDH-60T4-E 验证。 | |||
| ## 12. 完成交付与演示 | |||
| - 整理规格书、方案书、测试大纲、构建运行说明和演示工程。 | |||