适用代码状态:2026-08-29
这份文档用于按功能逐章阅读当前代码。它不是开发计划,也不是需求清单。这里列出的功能都能在当前源码中找到实现;每章最后的复选框用于记录阅读进度。
当前代码可以按 17 个功能模块 阅读。前 4 章建立公共模型和工程基础,第 5~12 章是编辑功能,第 13~16 章是运行与 PLC 链路,第 17 章是用户运行程序交付链路。
| 顺序 | 功能模块 | 主要解决的问题 |
|---|---|---|
| 1 | 程序启动与应用配置 | 程序如何创建对象、加载配置并决定启动为编程器还是用户运行版 |
| 2 | M/D 寄存器公共模型 | 地址、四种 D 数据类型、编解码和统一仓库接口如何工作 |
| 3 | 工程聚合、数量边界与校验 | 一个工程保存什么,以及“可保存”和“可运行”的区别 |
| 4 | 工程新建、保存、加载与 JSON | 工程操作如何原子完成,严格 JSON 4.0 如何读写 |
| 5 | 主窗口、工程树与编辑会话 | UI 如何组织页面、逻辑、属性面板和动作状态 |
| 6 | HMI 页面管理 | 页面新增、改名、排序、尺寸、初始页和安全删除 |
| 7 | HMI 控件编辑 | 8 类控件的添加、属性、绑定、移动、对齐、删除和历史 |
| 8 | HMI 运行交互与页面导航 | 控件如何读取/写入活动仓库,按钮、状态文本和页面跳转如何运行 |
| 9 | 报警、报警列表与地址注释 | 报警定义/记录以及 M/D 地址备注如何编辑和显示 |
| 10 | 连续梯形图模型与逻辑组 | 10 个条件格、输出槽、竖线、网络和多逻辑组的数据结构 |
| 11 | 梯形图画布与编辑操作 | 行、横竖线、框选、删除、复制粘贴、撤销重做和网络注释 |
| 12 | 梯形图指令、命令语与检查 | 触点、线圈、比较、MOVE、ADD/SUB、连续输入和语法检查 |
| 13 | 软件逻辑执行与离线仿真 | 扫描语义、边沿状态、运行轨迹、定时扫描和离线初始值 |
| 14 | 数据监控 | 编辑态/运行态监控、批量地址、四种 D 类型和数据源显式切换 |
| 15 | PLC 配置、搜索与 Modbus RTU | 串口参数、自动搜索、异步轮询、缓存、写入、故障和恢复 |
| 16 | 运行模式与运行监控窗口 | 编辑/离线/真机切换、活动仓库、真机本地推算轨迹和运行大屏 |
| 17 | 用户运行程序导出与启动 | HMI 工程裁剪、exe 尾部封装、依赖复制、自动连接和自动重连 |
推荐总顺序就是表格中的 1 -> 17。不要一上来通读 main_window.cpp:它是窗口级协调层,里面同时出现多数功能入口,缺少前面的模型和服务背景时很容易看乱。
领域模型/规则 -> 服务用例 -> 基础设施实现(若有) -> UI 入口/投影 -> 对应测试
domain:数据长什么样、什么情况算合法,不依赖 Qt、串口或文件系统services:一次完整业务操作怎么执行、失败如何回滚、需要哪些外部契约infrastructure:JSON、INI、exe 封装和 Qt Modbus 等技术实现ui:收集用户输入、调用服务、刷新画面,不应重新实现业务规则tests:当前代码真正承诺了哪些边界和失败行为每章至少做到下面四件事,再勾选章节完成项:
config/application.iniconfig/runtime.inimain.cpp 中创建所有领域仓库、服务、基础设施和主窗口,并完成依赖注入app/src/services/application_settings.happ/src/infrastructure/application_settings_loader.h/.cppapp/src/infrastructure/runtime_settings_loader.h/.cppapp/src/main.cppdocs/用户使用/应用配置说明.mdmain()
-> RuntimeProjectBundleService::load()
-> ApplicationSettingsLoader::load() 或 RuntimeSettingsLoader::load()
-> 构造 ProjectService / 编辑服务 / 寄存器仓库 / 运行服务
-> RuntimeModeService::configurePlc()
-> MainWindow
-> QApplication::exec()
M0~M4000 位地址和 D0~D4000 字地址0 开始的原始地址Int16、Int32、Float32、Float64Float64 起始地址要求偶数;所有多字范围不能越过 D4000app/src/domain/register_address.h/.cppapp/src/domain/register_value_type.h/.cppapp/src/domain/register_repository.happ/src/domain/virtual_register_repository.h/.cppapp/src/infrastructure/plc_register_repository.h/.cppapp/src/domain/active_register_repository.h/.cppdocs/二次开发/信捷D寄存器与浮点扩展说明.mdHMI / 监控 / 软件执行器
-> RegisterRepository
-> VirtualRegisterRepository(离线)
-> PlcRegisterRepository(真机读回缓存 + 异步写回调)
-> ActiveRegisterRepository(按模式转发)
Project 聚合元数据、HMI 页面、初始页、报警、地址注释和控制逻辑project_limits.h,INI 只能收紧部分上限validate() 允许保存未配置完成的编辑草稿validateForRunning() 进一步拒绝未绑定控件、未配置节点和不可运行结构app/src/domain/project_limits.happ/src/domain/project_model.h/.cppapp/src/domain/hmi_model.h/.cppapp/src/domain/alarm_model.h/.cppapp/src/domain/control_logic_model.h/.cppdocs/用户使用/数量边界确认方案.md| 项目 | 上限 |
|---|---|
| HMI 页面 | 32 |
| 每页 HMI 控件 | 128 |
| 全工程 HMI 控件 | 2048 |
| 报警定义 | 256 |
| 控制逻辑组 | 32 |
| 每组梯形图行 | 256 |
| 全工程梯形图行 | 2048 |
| 每行条件格 | 固定 10 |
| 状态文本 D 区间 | 16 |
| PLC 去重轮询地址 | 256 |
| PLC 轮询块 | 8 |
| 单个工程文件 | 16 MiB |
formatVersion: "4.0"1.0/2.0/3.0 和未知版本直接拒绝,不迁移、不兼容app/src/domain/project_storage.happ/src/services/project_service.h/.cppapp/src/infrastructure/json_project_storage.h/.cppdocs/工程格式说明.mdapp/tests/project_management_tests.cppMainWindow::saveProject()/loadProject()
-> ProjectService::save()/load()
-> Project::validate()
-> JsonProjectStorage::save()/load()
-> 文件系统
ModePolicy 统一控制编辑态和运行态动作是否可用app/src/ui/main_window.uiapp/src/ui/main_window.happ/src/ui/project_workspace_controller.h/.cppapp/src/ui/property_panel_controller.h/.cppapp/src/ui/main_window.cpp 中的构造、configure* 和 update*Ui 部分app/src/ui/toolbar_icon_factory.h/.cpp| 类 | 职责 |
|---|---|
MainWindow |
窗口级动作、文件对话框、控制器组合、模式请求和运行版导出 |
ProjectWorkspaceController |
工程树、当前页面/逻辑、增删改排序和刷新编辑器 |
PropertyPanelController |
展示并提交页面、控件和节点属性 |
RuntimePanelController |
运行窗口生命周期和运行数据投影 |
HmiPage:app/src/domain/hmi_model.h/.cppHmiEditorService 的页面方法:app/src/services/hmi_editor_service.h/.cppProjectWorkspaceController 的页面操作PropertyPanelController::showPageProperties() 及页面属性提交MainWindow 中页面 QAction 的连接ensureDefaultPage()
addPage()
renamePage()
resizePage()
movePage()
setInitialPage()
removePage()
当前共有 8 类 HMI 控件:
| 控件 | 绑定/职责 |
|---|---|
按钮 Button |
固定绑定 M,写入 M 位 |
指示灯 Indicator |
固定绑定 M,只读显示 |
数值显示 NumericDisplay |
固定绑定 D,支持四种数值类型 |
数值输入 NumericInput |
固定绑定 D,支持四种数值类型和写入 |
文本 Label |
固定文字,不绑定寄存器 |
状态文本 StatusText |
M OFF/ON 文本或 D 连续区间映射,只读 |
页面跳转 PageJump |
跳转到指定 HMI 页面 |
报警列表 AlarmList |
显示当前会话报警记录 |
共同编辑能力包括添加、自动分配绑定地址、选择、移动、调整尺寸、编辑文字和外观、删除、六方向批量对齐、撤销和重做。控件必须完整位于页面边界内,一次批量操作要么全部成功,要么完全不修改。
app/src/domain/hmi_control_registry.h/.cppHmiControl 和各专用配置:app/src/domain/hmi_model.h/.cppapp/src/services/hmi_editor_service.h/.cppapp/src/ui/hmi_editor_widget.h/.cppapp/src/ui/property_panel_controller.h/.cppapp/src/ui/status_text_dialog.ui/.h/.cppapp/src/ui/button_extension_dialog.ui/.h/.cppdocs/用户使用/HMI控件绑定说明.mddocs/用户使用/状态文本控件说明.mddocs/用户使用/按钮启用条件说明.md[下限, 上限),必须连续覆盖整个数值域,最多 16 段--app/src/services/hmi_runtime_service.h/.cppapp/src/services/hmi_navigation_service.h/.cppapp/src/ui/hmi_editor_widget.cpp 中运行态绘制、鼠标事件和值刷新部分app/src/ui/runtime_monitor_widget.h/.cppapp/src/domain/active_register_repository.h/.cpp运行画布上的按钮事件
-> HmiRuntimeService::evaluateButtonEnabled()
-> HmiRuntimeService::operateButton()
-> ActiveRegisterRepository
-> VirtualRegisterRepository 或 PlcRegisterRepository
MOn、MOff、DHigh、DLowapp/src/domain/alarm_model.h/.cppapp/src/services/alarm_editor_service.h/.cppapp/src/services/alarm_service.h/.cppapp/src/ui/alarm_configuration_dialog.ui/.h/.cppapp/src/services/register_comment_service.h/.cppapp/src/ui/register_comment_dialog.ui/.h/.cppapp/src/ui/runtime_monitor_widget.cpp 和 HMI 报警列表投影Gap、Wire 或 Node0~10 列边界,长竖线由多段组成networkHeadIndex() 是查找网络首行和网络注释归属的统一入口app/src/domain/control_logic_model.happ/src/domain/control_logic_model.cpp 的结构校验和网络计算app/src/services/logic_editor_service.h 的查询与逻辑组管理接口LogicEditorService 中 ensureDefaultLogic/addLogic/renameLogic/removeLogic/moveLogic/setLogicEnabledProjectWorkspaceController 中控制逻辑操作docs/用户使用/连续梯形图网格说明.mdControlLogic
|- rungs[]
| |- cells[10]: Gap / Wire / Node
| `- output: optional LogicNode
`- verticalConnections[]: 相邻行之间的竖线段
app/src/services/editor_history.happ/src/services/logic_editor_service.h 中行、线、选择、剪贴板和历史的数据结构app/src/services/logic_editor_service.cpp 对应方法app/src/ui/logic_editor_widget.h/.cpp 的布局、命中、选择、拖动预览和绘制app/src/ui/main_window.cpp 中梯形图 QAction 和快捷键连接docs/用户使用/鼠标画线与删线说明.mddocs/用户使用/连续梯形图网格说明.mdaddRung/insertRungAbove/insertRungBelow/removeRungsetHorizontalWireRange/setVerticalConnectionRange 和两个 apply*AndAdvancedeleteSelection/removeNodes/removeVerticalConnectionscopySelection/pasteClipboard/undo/redo条件指令:
| 类型 | 语义 |
|---|---|
| 常开触点 | M 为 ON 时导通 |
| 常闭触点 | M 为 OFF 时导通 |
| 上升沿触点 | M 从 OFF 变 ON 的一次扫描脉冲 |
| 下降沿触点 | M 从 ON 变 OFF 的一次扫描脉冲 |
| D 比较 | D 的 Int16 值与常量执行六种比较 |
输出和数据指令:
| 类型 | 语义 |
|---|---|
| 普通线圈 OUT | 每次扫描把网络结果写入 M |
| 置位线圈 SET | 网络成立时将 M 置 ON |
| 复位线圈 RST | 网络成立时将 M 置 OFF |
| MOVE | 常量或 D 源写入一个 D 目标 |
| ADD | 两个常量/D 操作数相加,Int16 饱和 |
| SUB | 两个常量/D 操作数相减,Int16 饱和 |
编辑器还支持画布内命令语输入与补全:LD/LDI/LDP/LDF、比较、AND/ANI、OR/ORI、OUT/SET/RST/MOV/ADD/SUB。语法检查会规整不参与完整输出路径的残线并定位断路输出;双线圈检查是独立动作。
LogicNodeConfig 各 variant:app/src/domain/control_logic_model.h/.cppapp/src/services/logic_command_service.h/.cppLogicEditorService 的节点、输出、并联、语法检查和双线圈方法app/src/ui/logic_instruction_dialog.ui/.h/.cppapp/src/ui/logic_editor_widget.cpp 中内嵌命令输入部分docs/用户使用/命令语输入说明.mdapp/src/services/software_logic_executor.h/.cppapp/src/services/offline_simulation_service.h/.cppapp/src/domain/virtual_register_repository.h/.cppapp/src/ui/logic_editor_widget.cpp 中运行轨迹绘制app/src/ui/runtime_panel_controller.cpp 中离线刷新app/tests/offline_simulation_service_tests.cppRuntimeModeService::enterOfflineRunning()
-> 运行前工程校验/语法检查
-> OfflineSimulationService::start(logic snapshot)
-> QTimer
-> SoftwareLogicExecutor::executeScan()
-> VirtualRegisterRepository
-> LogicTraceSnapshot
-> RuntimePanelController -> LogicEditorWidget
app/src/domain/register_monitor_model.h/.cppapp/src/services/register_monitor_service.h/.cppapp/src/ui/free_monitor_widget.ui/.h/.cppapp/src/ui/main_window.cpp 中数据监控配置与刷新app/src/ui/runtime_monitor_widget.cpp 中运行监控接入docs/用户使用/数据监控与离线初始值说明.mdapp/tests/register_monitor_service_tests.cppQModbusRtuSerialMaster 异步连接,不阻塞 UIapp/src/services/plc_communication_gateway.happ/src/infrastructure/plc_register_repository.h/.cppapp/src/infrastructure/plc_communication_error_classifier.h/.cppapp/src/infrastructure/plc_communication_service.h/.cppapp/src/services/plc_discovery_gateway.h/.cppapp/src/infrastructure/plc_discovery_service.h/.cppapp/src/ui/plc_connection_dialog.ui/.h/.cppdocs/architecture.md 的“Modbus RTU 通信”docs/XDH-60T4-E指令与Modbus要点.mdPLC
<-> PlcCommunicationService(异步 Modbus RTU、轮询、恢复)
<-> PlcRegisterRepository(最新读回缓存、写请求转交)
<-> ActiveRegisterRepository
<-> HMI / 数据监控
真机代码阅读不等于真机操作。需要实际写 PLC 时,必须按项目约定先读原值、在 STOP 状态验证、测试后恢复并再次读回。
app/src/domain/runtime_state.h/.cppapp/src/services/runtime_mode_service.h/.cppapp/src/services/online_logic_monitor_service.h/.cppapp/src/ui/runtime_monitor_window.ui/.h/.cppapp/src/ui/runtime_monitor_widget.ui/.h/.cppapp/src/ui/runtime_panel_controller.h/.cppapp/src/ui/main_window.cpp 的 requestMode() 和 updateModeUi()docs/architecture.md 的“数据源与运行模式”| 模式 | 工程编辑 | HMI/监控数据源 | 本地逻辑 |
|---|---|---|---|
| 编辑态 | 允许 | 数据监控由用户显式选离线或 PLC | 停止 |
| 离线运行 | 禁止 | 虚拟 M/D | 执行并写虚拟 M/D |
| 真机运行 | 禁止 | PLC 读回缓存 | 用缓存副本推算,只写临时仓库 |
runtime.iniruntime.ini 连接真实 PLC,首读完成后才放行 HMI 写入ProjectService::exportHmiRuntimeAs():app/src/services/project_service.cppapp/src/infrastructure/runtime_project_bundle.h/.cppapp/src/infrastructure/runtime_settings_loader.h/.cppapp/src/ui/main_window.cpp 的 exportRuntimeProgram() 及辅助流程app/src/main.cpp 的封装工程识别和临时加载流程MainWindow 的用户运行版构造、自动连接和重连定时器scripts/package_qt_app.ps1,理解编程器发布打包与“导出运行版”的区别docs/用户使用/用户运行程序导出说明.md编程器导出
-> ProjectService::exportHmiRuntimeAs()
-> JsonProjectStorage
-> RuntimeProjectBundleService::write()
-> 复制运行依赖和 runtime.ini
导出 exe 启动
-> RuntimeProjectBundleService::load(current exe)
-> RuntimeSettingsLoader::load()
-> ProjectService::load(temporary json)
-> HMI-only MainWindow
-> 自动连接 PLC -> 首读 -> 开放 HMI 写入
这部分不是第 18 个业务功能,而是看完 17 个功能后用于串联全项目。
| 测试目标 | 主要覆盖 |
|---|---|
domain_tests |
地址、模型、边界、编解码和梯形图可达性 |
application_settings_tests |
普通编程器配置 |
runtime_settings_tests |
用户运行版最小配置 |
alarm_service_tests |
报警定义和运行记录 |
hmi_editor_service_tests |
HMI 页面、控件、历史、导航和运行读写 |
logic_editor_service_tests |
连续梯形图全部编辑用例 |
offline_simulation_service_tests |
软件扫描、离线仿真和真机缓存副本推算 |
project_management_tests |
JSON、工程服务和非法文件 |
register_monitor_service_tests |
数据监控和离线初始值 |
runtime_project_bundle_tests |
exe 工程封装 |
runtime_mode_service_tests |
三种模式和仓库切换 |
runtime_panel_controller_tests |
运行窗口、轨迹投影和关键 UI 交互 |
plc_connection_dialog_tests |
PLC 配置与自动搜索 UI |
plc_runtime_tests |
PLC 缓存、轮询、写入和恢复状态机 |
pwsh -NoLogo -NoProfile -File .\scripts\run_qt_tests.ps1 -Configuration Release
pwsh -NoLogo -NoProfile -File .\scripts\run_qt_tests.ps1 -Configuration Release -Suite Performance
pwsh -NoLogo -NoProfile -File .\scripts\build_and_run_qt.ps1
git diff --check
阅读时遇到下面内容,不要继续在代码里找,因为当前项目明确不实现:
1.0/2.0/3.0每完成一章,建议在“备注”里写下仍没想明白的问题或关键调用链,后续回看会很省时间。
| 完成 | 章节 | 备注 |
|---|---|---|
| [ ] | 1. 程序启动与应用配置 | |
| [ ] | 2. M/D 寄存器公共模型 | |
| [ ] | 3. 工程聚合、数量边界与校验 | |
| [ ] | 4. 工程新建、保存、加载与 JSON | |
| [ ] | 5. 主窗口、工程树与编辑会话 | |
| [ ] | 6. HMI 页面管理 | |
| [ ] | 7. HMI 控件编辑 | |
| [ ] | 8. HMI 运行交互与页面导航 | |
| [ ] | 9. 报警、报警列表与地址注释 | |
| [ ] | 10. 连续梯形图模型与逻辑组 | |
| [ ] | 11. 梯形图画布与编辑操作 | |
| [ ] | 12. 梯形图指令、命令语与检查 | |
| [ ] | 13. 软件逻辑执行与离线仿真 | |
| [ ] | 14. 数据监控 | |
| [ ] | 15. PLC 配置、搜索与 Modbus RTU | |
| [ ] | 16. 运行模式与运行监控窗口 | |
| [ ] | 17. 用户运行程序导出与启动 |
全部完成后,你应该能够从任意一个界面动作出发,快速判断它属于哪个控制器、调用哪个服务、修改哪个领域对象、是否经过基础设施,以及由哪个测试证明。