| @@ -11,6 +11,9 @@ | |||
| - 状态文本只读;离线从虚拟寄存器读取,真机从 PLC 轮询缓存读取;绑定缺失、仓库不可用、读取失败和 NaN/Inf 统一显示 `--` | |||
| - 编辑画布固定显示“状态文本”,运行画布显示映射结果;字号、颜色、粗体、斜体复用现有 HMI 外观属性 | |||
| - 状态文本配置使用独立 Qt Designer 对话框;属性面板隐藏普通绑定和数值类型编辑入口,改用“状态映射”专用配置按钮 | |||
| - HMI 控件按类型自动固定绑定区域:按钮/指示灯为 M,数值显示/输入为 D;新增控件自动分配当前区域的顺序地址,属性面板只修改地址编号,状态文本仍在专用对话框选择 M 或 D | |||
| - HMI 按钮保留置 ON、置 OFF、翻转和点动四种操作,并可附加一个 M 位或 D 数值启用条件;条件不满足或读取失败时按钮置灰且禁止操作 | |||
| - 按钮启用条件配置使用独立 Qt Designer 对话框;支持 M 地址 + OFF/ON、D 地址 + 四种数据类型 + 六种比较方式 + 比较值 | |||
| ## 领域与运行链路 | |||
| @@ -18,8 +21,8 @@ | |||
| - D 状态文本参与完整连续 D 字范围的冲突检查,也参与 16 位 MOVE/ADD/SUB 写入保护 | |||
| - `HmiRuntimeService::readStatusText()` 统一执行 M 位文本映射和 D 半开区间映射,不提供写入接口 | |||
| - `RuntimeModeService` 将 D 状态文本占用的全部连续字加入 PLC 轮询地址和 `RegisterWordRange` | |||
| - JSON 已升级为唯一 `formatVersion: "3.0"`;状态文本保存为 `statusText.source`、M 的 `offText/onText` 或 D 的 `ranges` | |||
| - 旧 `1.0`、`2.0` 和其他版本工程直接拒绝,不提供迁移、双写、回退或兼容适配 | |||
| - JSON 已升级为唯一 `formatVersion: "4.0"`;按钮保存为 `buttonEnableCondition`,状态文本保存为 `statusText.source`、M 的 `offText/onText` 或 D 的 `ranges` | |||
| - 旧 `1.0`、`2.0`、`3.0` 和其他版本工程直接拒绝,不提供迁移、双写、回退或兼容适配 | |||
| ## 关键文件 | |||
| @@ -28,8 +31,11 @@ | |||
| - 工程持久化:`app/src/infrastructure/json_project_storage.*` | |||
| - 编辑服务和属性面板:`app/src/services/hmi_editor_service.cpp`、`app/src/ui/property_panel_controller.*` | |||
| - 状态文本对话框:`app/src/ui/status_text_dialog.*` | |||
| - 按钮启用条件对话框:`app/src/ui/button_extension_dialog.*` | |||
| - HMI 画布和工具栏:`app/src/ui/hmi_editor_widget.cpp`、`main_window.cpp/.ui`、`toolbar_icon_factory.*` | |||
| - 用户说明:`docs/用户使用/状态文本控件说明.md` | |||
| - 用户说明:`docs/用户使用/HMI控件绑定说明.md` | |||
| - 用户说明:`docs/用户使用/按钮启用条件说明.md` | |||
| - 格式契约:`docs/工程格式说明.md` | |||
| ## 验证结果 | |||
| @@ -37,6 +43,8 @@ | |||
| - 13 个 Release Functional 测试目标全部通过:领域、设置、报警、HMI 编辑、逻辑编辑、离线仿真、工程管理、监控、运行包、运行模式、运行面板、PLC 对话框、PLC 运行时 | |||
| - HMI 编辑专项覆盖六种对齐方向、批量对齐单步撤销/重做、非法选择原子性和无变化不产生历史记录 | |||
| - 状态文本专项覆盖 M OFF/ON 映射、D 四类型区间边界、区间间隙/重叠、整数边界、NaN/Inf、只读约束、16 区间上限、JSON 往返和旧 `2.0` 拒绝 | |||
| - 按钮专项覆盖 M/D 条件满足与不满足、服务层二次拒绝、点动条件变化后的释放复位、条件 JSON 往返和条件引用地址轮询 | |||
| - 属性面板专项覆盖按钮/数值控件自动分配连续默认地址,以及手动修改地址编号后保持固定区域 | |||
| - 运行模式专项覆盖 Double 状态文本的 `D70~D73` 轮询地址及四字 `RegisterWordRange` | |||
| - `git diff --check` 已通过;当前尚未连接真实 PLC,本轮功能为只读映射并复用既有缓存/轮询链路,不需要改写设备 | |||
| - Release 主程序已在本轮最终代码上重新构建;设置 Qt offscreen 环境后启动 3 秒仍保持事件循环,验证结束已关闭进程 | |||
| @@ -49,5 +57,6 @@ | |||
| ## 后续人工检查 | |||
| - 打开 HMI 页面添加“状态文本”,确认属性面板的专用配置按钮、M/D 切换和区间表布局在常用缩放下无重叠 | |||
| - 打开 HMI 页面添加按钮,分别配置 M 位和 D 数值启用条件,确认条件不满足时按钮置灰且无法点击,满足后恢复可操作 | |||
| - 离线运行写入虚拟 M/D,确认 M OFF/ON 和 D 在 30、80 边界的显示结果;停止运行后恢复编辑占位词 | |||
| - 设备可用时在 STOP 状态读取原值,确认状态文本使用的 M/D 被轮询读回;测试只读,不向设备写入状态文本数据 | |||
| @@ -72,10 +72,12 @@ Project | |||
| - HMI 控件类型、默认值、绑定规则和运行值类型由无 Qt 依赖的 `hmi_control_registry` 统一描述 | |||
| - HMI 数值控件通过 `RegisterDataType` 选择 `Int16`、`Int32`、`Float32` 或 `Float64`;底层仓库始终保存原始 16 位 D 字,统一编解码层按低地址低字顺序组合 1、2 或 4 个连续 D | |||
| - 状态文本是独立只读控件:M 配置 OFF/ON 文本,D 配置覆盖全域的连续半开区间;离线读取虚拟仓库,真机读取 PLC 缓存,不可用时显示 `--` | |||
| - HMI 控件根据控件描述符自动使用固定绑定区域:按钮/指示灯使用 M,数值显示/输入使用 D;新增控件从当前区域最大已用地址后继续递增,属性面板只保留地址编号输入,状态文本的 M/D 选择仍属于专用状态映射配置 | |||
| - HMI 按钮保留原有四种 M 位操作,并可附加一个 M 位或 D 数值启用条件;条件不满足或读回失败时运行画面置灰且禁止操作,条件引用地址纳入 PLC 轮询 | |||
| - 需要寄存器的 HMI 控件、报警条件和梯形图节点直接保存 M/D 地址;项目不再维护统一 `dataPoints` 表 | |||
| - `registerComments` 只是 M/D 地址元数据,不保存当前值或引用位置,也不会让地址自动进入 PLC 轮询 | |||
| 工程 JSON 格式固定为严格的 `3.0`。加载顺序为 JSON 解析、版本检查、字段解析、领域校验,全部成功后才能替换当前工程;保存使用原子提交。旧 `1.0/2.0` 文件直接拒绝,不做迁移或双写。字段定义见 `docs/工程格式说明.md`。 | |||
| 工程 JSON 格式固定为严格的 `4.0`。加载顺序为 JSON 解析、版本检查、字段解析、领域校验,全部成功后才能替换当前工程;保存使用原子提交。旧 `1.0/2.0/3.0` 文件直接拒绝,不做迁移或双写。字段定义见 `docs/工程格式说明.md`。 | |||
| 校验分两级: | |||
| @@ -1,14 +1,14 @@ | |||
| # 工程 JSON 3.0 格式说明 | |||
| # 工程 JSON 4.0 格式说明 | |||
| ## 格式策略 | |||
| 当前只有一个有效工程格式:`3.0`。代码直接读写本文描述的结构,不为 `1.0`、`2.0` 或早期开发草稿增加迁移分支、字段推断或双写逻辑;旧版本加载会明确失败。缺少必填字段、字段类型错误、引用不存在或领域校验失败时,加载必须失败且不得替换当前工程。 | |||
| 当前只有一个有效工程格式:`4.0`。代码直接读写本文描述的结构,不为 `1.0`、`2.0`、`3.0` 或早期开发草稿增加迁移分支、字段推断或双写逻辑;旧版本加载会明确失败。缺少必填字段、字段类型错误、引用不存在或领域校验失败时,加载必须失败且不得替换当前工程。 | |||
| ## 顶层结构 | |||
| ```json | |||
| { | |||
| "formatVersion": "3.0", | |||
| "formatVersion": "4.0", | |||
| "id": "project-1", | |||
| "name": "包装线", | |||
| "hmiPages": [], | |||
| @@ -54,7 +54,7 @@ | |||
| | `type` | 绑定 | 额外必填字段 | | |||
| | --- | --- | --- | | |||
| | `button` | M 地址或 `null` 草稿 | `buttonOperation` | | |||
| | `button` | M 地址或 `null` 草稿 | `buttonOperation`、`buttonEnableCondition` | | |||
| | `indicator` | M 地址或 `null` 草稿 | 无 | | |||
| | `numericDisplay` | D 地址或 `null` 草稿 | `dataType` | | |||
| | `numericInput` | D 地址或 `null` 草稿 | `dataType` | | |||
| @@ -63,7 +63,7 @@ | |||
| | `pageJump` | 必须为 `null` | `targetPageId` | | |||
| | `alarmList` | 必须为 `null` | 无 | | |||
| `type` 只接受表中列出的控件类型。其他类型按严格 `3.0` 规则直接拒绝加载,不做兼容迁移。 | |||
| `type` 只接受表中列出的控件类型。其他类型按严格 `4.0` 规则直接拒绝加载,不做兼容迁移。 | |||
| 数值显示和数值输入控件必须额外保存 `dataType`,值只能是 `int16`、`int32`、`float32` 或 `float64`: | |||
| @@ -90,6 +90,27 @@ | |||
| 多字值统一按信捷字序保存:低地址放低 16 位,地址递增时依次保存更高的 16 位。数值控件和 D 状态文本之间允许相同起始地址和相同类型重复绑定,部分重叠或同起始地址不同类型会被拒绝。要求 `dataType` 的控件缺少该字段或使用 `int64` 等未知枚举时加载失败。 | |||
| 按钮除了自身的 M 位操作外,还可以保存一个可选的启用条件。没有条件时,`buttonEnableCondition` 必须为 JSON `null`;有条件时只能保存一个 `mBit` 或 `dValue` 条件: | |||
| ```json | |||
| { | |||
| "id": "start-button", | |||
| "type": "button", | |||
| "bounds": {"x": 20, "y": 20, "width": 120, "height": 40}, | |||
| "text": "启动", | |||
| "binding": {"area": "M", "index": 10}, | |||
| "properties": {}, | |||
| "buttonOperation": "momentaryOn", | |||
| "buttonEnableCondition": { | |||
| "type": "mBit", | |||
| "address": {"area": "M", "index": 0}, | |||
| "expected": false | |||
| } | |||
| } | |||
| ``` | |||
| `mBit` 使用 `expected` 判断 M 位 ON/OFF;`dValue` 还必须提供 `dataType`、`operation` 和有限数值 `value`。`operation` 只能是 `equal`、`notEqual`、`lessThan`、`lessThanOrEqual`、`greaterThan` 或 `greaterThanOrEqual`。Int16/Int32 的比较值必须是对应范围内的整数,Float32/Float64 必须是可表示的有限值。条件地址及其连续 D 字会加入真机 PLC 轮询;条件读回失败时运行画面将按钮置灰并禁止操作。 | |||
| 状态文本是独立只读控件。M 模式保存 OFF/ON 文本: | |||
| ```json | |||
| @@ -184,7 +205,7 @@ AlarmList 只负责显示项目级当前报警,不保存独立触发逻辑, | |||
| } | |||
| ``` | |||
| 注释不保存寄存器当前值,也不自动把地址加入 Modbus 轮询。网络注释使用网络首行的 `LadderRung.comment` 保存,与行 ID、名称、连续网格和输出并列;通过任意竖线连接到上一行的支路行必须保存空字符串。同一网络只有首行注释可以非空,违反该规则的 `3.0` 工程直接拒绝加载,不迁移或回退。网络注释允许为空;非空时只能使用单行文本,不能包含回车或换行,且不能超过 128 个 UTF-8 字节: | |||
| 注释不保存寄存器当前值,也不自动把地址加入 Modbus 轮询。网络注释使用网络首行的 `LadderRung.comment` 保存,与行 ID、名称、连续网格和输出并列;通过任意竖线连接到上一行的支路行必须保存空字符串。同一网络只有首行注释可以非空,违反该规则的 `4.0` 工程直接拒绝加载,不迁移或回退。网络注释允许为空;非空时只能使用单行文本,不能包含回车或换行,且不能超过 128 个 UTF-8 字节: | |||
| ```json | |||
| { | |||
| @@ -217,7 +238,7 @@ AlarmList 只负责显示项目级当前报警,不保存独立触发逻辑, | |||
| 控制逻辑初始可以没有行,`rungs` 为空不是错误。编辑器第一次添加条件、横线或输出时才创建行;删除最后一行后可以再次保存为空逻辑。没有输出的空行草稿允许保存 10 个 `Gap` 网格。输出行也允许暂存未完成的条件路径;运行时 `Gap` 会按断路处理,只有实际导通的输出槽才执行。 | |||
| 3.0 使用连续网格模型。每条 `LadderRung` 固定保存 10 个 `cells`,每格的 `kind` 是 `gap`、`wire` 或 `node`;条件节点直接嵌在 `node` 格中。行不再是互相隔离的网络容器,行间连接由控制逻辑的 `verticalConnections` 保存。长竖线由多个相邻连接对象组成,插入行会拆分原连接,删除行只在上下两段同列存在时合并。3.0 不把 `ConditionExpression` 写入工程 JSON,也不读取旧 `condition` 字段: | |||
| 4.0 使用连续网格模型。每条 `LadderRung` 固定保存 10 个 `cells`,每格的 `kind` 是 `gap`、`wire` 或 `node`;条件节点直接嵌在 `node` 格中。行不再是互相隔离的网络容器,行间连接由控制逻辑的 `verticalConnections` 保存。长竖线由多个相邻连接对象组成,插入行会拆分原连接,删除行只在上下两段同列存在时合并。4.0 不把 `ConditionExpression` 写入工程 JSON,也不读取旧 `condition` 字段: | |||
| ```json | |||
| {"id":"vertical-1","upperRungId":"rung-1","lowerRungId":"rung-2","columnBoundary":2} | |||
| @@ -249,4 +270,4 @@ MOVE、ADD 和 SUB 只能把结果写入 D 地址,源操作数和算术左右 | |||
| 工程保存允许未绑定的寄存器控件和未完成梯形图草稿。进入离线运行还必须通过运行校验:所有 HMI 寄存器控件完成绑定、所有 PageJump 目标有效、所有报警定义合法、所有启用逻辑中的节点已配置;`Gap` 仍表示断路并会让该支路不导通,不会被当成隐含横线。禁用草稿逻辑不阻止运行。左侧电源线直接接通的输出必须保存为 10 个 `Wire` 网格加输出槽。 | |||
| 当前实现只读写本文定义的严格 `3.0`。`1.0`、`2.0` 或其他旧工程不会自动迁移,加载时直接返回不支持的格式版本。 | |||
| 当前实现只读写本文定义的严格 `4.0`。`1.0`、`2.0` 或其他旧工程不会自动迁移,加载时直接返回不支持的格式版本。 | |||
| @@ -4,7 +4,7 @@ | |||
| ## 当前进度 | |||
| 第 1~11 步的软件实现和现场验证已完成,工程格式统一为包含连续网格和状态文本的 `3.0`,不保留旧工程兼容代码。当前重点是第 12 步的交付资料和最终演示整理。 | |||
| 第 1~11 步的软件实现和现场验证已完成,工程格式统一为包含连续网格、状态文本和按钮启用条件的 `4.0`,不保留旧工程兼容代码。当前重点是第 12 步的交付资料和最终演示整理。 | |||
| ## 1. 建立开发基线 | |||
| @@ -0,0 +1,14 @@ | |||
| # HMI 控件绑定说明 | |||
| HMI 控件的寄存器区域由控件类型固定,不需要在属性面板中手动选择 M 或 D: | |||
| | 控件 | 固定区域 | 用途 | | |||
| | --- | --- | --- | | |||
| | 按钮 | M | 按钮操作写入的 M 位 | | |||
| | 指示灯 | M | 读取显示的 M 位 | | |||
| | 数值显示 | D | 读取显示的 D 数值 | | |||
| | 数值输入 | D | 写入的 D 数值 | | |||
| 添加上述控件时,软件会从当前区域的最大已用地址继续加一:第一个按钮或指示灯使用 `M0`,下一个使用 `M1`;第一个数值控件使用 `D0`,下一个使用 `D1`。多字数值会按实际占用字数继续递增,并自动跳到合法的起始地址。属性面板只显示当前地址编号,用户可以直接修改编号,区域始终由控件类型决定。 | |||
| 文本、页面跳转和报警列表不绑定寄存器。状态文本同时支持 M 状态和 D 数值两种来源,区域选择仍在“状态映射”配置对话框中完成。按钮的启用条件是独立配置,仍可引用 M 或 D,不改变按钮自身固定为 M 的规则。 | |||
| @@ -0,0 +1,28 @@ | |||
| # 按钮启用条件说明 | |||
| ## 用途 | |||
| 按钮可以附加一个启用条件。条件满足时按钮保持原有外观并允许操作;条件不满足或条件数据无法读取时,按钮会置灰并禁止鼠标操作。一个按钮最多配置一个条件。 | |||
| 按钮自身的 M 位操作仍可选择“置 ON”“置 OFF”“翻转”或“点动”,启用条件只负责决定当前是否允许触发该操作。 | |||
| ## 配置方式 | |||
| 选中 HMI 按钮,在属性面板的“启用条件”处点击“未设置”或已有条件摘要,打开“按钮扩展配置”对话框。可选择以下三种状态: | |||
| - **未设置**:按钮不增加限制,运行态按照原有按钮操作规则执行 | |||
| - **M 位条件**:选择 M 地址和要求状态 `OFF` 或 `ON`,只有实际 M 位等于要求状态时按钮可用 | |||
| - **D 数值条件**:选择 D 起始地址、`Int16`、`Int32`、`Float32` 或 `Double (Float64)`,再选择等于、不等于、小于、小于等于、大于或大于等于,并填写比较值 | |||
| Int16 和 Int32 的比较值必须是对应范围内的整数。Float32 和 Float64 的比较值必须是有限数值。多字 D 类型会读取从起始地址开始的完整连续 D 字。 | |||
| ## 运行行为 | |||
| - 离线运行从虚拟寄存器读取条件,真机运行从 PLC 轮询缓存读取条件 | |||
| - 条件引用的 M/D 地址会自动加入真机轮询集合 | |||
| - 条件不满足时,按钮显示为灰色,鼠标悬停和点击均不会触发写入 | |||
| - 条件读取失败、地址无效或数据无法解码时,按钮按不可用处理,不会误写按钮自身的 M 位 | |||
| - 服务层在按钮按下事件时会再次检查条件,避免仅依赖画布状态 | |||
| - 点动按钮按下后即使条件随后变化,释放事件仍会写入 OFF,保证点动复位安全 | |||
| 按钮启用条件只控制 HMI 是否允许操作,不会修改 PLC 程序,也不会向条件引用的 M/D 地址写入数据。 | |||
| @@ -64,4 +64,4 @@ | |||
| ## 工程文件 | |||
| 连续网格使用严格的工程 JSON `3.0`。旧 `1.0/2.0` 工程不自动迁移,加载时会提示格式版本不受支持。 | |||
| 连续网格使用严格的工程 JSON `4.0`。旧 `1.0/2.0/3.0` 工程不自动迁移,加载时会提示格式版本不受支持。 | |||
| @@ -51,4 +51,4 @@ | |||
| ## 数据与限制 | |||
| 每行固定持久化 10 个 `cells`,竖线持久化为相邻行之间的 `verticalConnections`。工程格式为严格 `3.0`,旧 `1.0/2.0` 文件直接拒绝,不写兼容迁移代码。画布像素、鼠标轨迹和悬空自由线段不会写入工程文件。 | |||
| 每行固定持久化 10 个 `cells`,竖线持久化为相邻行之间的 `verticalConnections`。工程格式为严格 `4.0`,旧 `1.0/2.0/3.0` 文件直接拒绝,不写兼容迁移代码。画布像素、鼠标轨迹和悬空自由线段不会写入工程文件。 | |||