diff --git a/docs/ai/handoff.md b/docs/ai/handoff.md index 2a967db..049ee50 100644 --- a/docs/ai/handoff.md +++ b/docs/ai/handoff.md @@ -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 被轮询读回;测试只读,不向设备写入状态文本数据 diff --git a/docs/architecture.md b/docs/architecture.md index 86a588f..04dca7c 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -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`。 校验分两级: diff --git a/docs/工程格式说明.md b/docs/工程格式说明.md index 187be66..96bcd64 100644 --- a/docs/工程格式说明.md +++ b/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` 或其他旧工程不会自动迁移,加载时直接返回不支持的格式版本。 diff --git a/docs/开发顺序.md b/docs/开发顺序.md index 32baa7a..6dff55e 100644 --- a/docs/开发顺序.md +++ b/docs/开发顺序.md @@ -4,7 +4,7 @@ ## 当前进度 -第 1~11 步的软件实现和现场验证已完成,工程格式统一为包含连续网格和状态文本的 `3.0`,不保留旧工程兼容代码。当前重点是第 12 步的交付资料和最终演示整理。 +第 1~11 步的软件实现和现场验证已完成,工程格式统一为包含连续网格、状态文本和按钮启用条件的 `4.0`,不保留旧工程兼容代码。当前重点是第 12 步的交付资料和最终演示整理。 ## 1. 建立开发基线 diff --git a/docs/用户使用/HMI控件绑定说明.md b/docs/用户使用/HMI控件绑定说明.md new file mode 100644 index 0000000..1359f1f --- /dev/null +++ b/docs/用户使用/HMI控件绑定说明.md @@ -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 的规则。 diff --git a/docs/用户使用/按钮启用条件说明.md b/docs/用户使用/按钮启用条件说明.md new file mode 100644 index 0000000..ecd77e9 --- /dev/null +++ b/docs/用户使用/按钮启用条件说明.md @@ -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 地址写入数据。 diff --git a/docs/用户使用/连续梯形图网格说明.md b/docs/用户使用/连续梯形图网格说明.md index 166a271..7471a02 100644 --- a/docs/用户使用/连续梯形图网格说明.md +++ b/docs/用户使用/连续梯形图网格说明.md @@ -64,4 +64,4 @@ ## 工程文件 -连续网格使用严格的工程 JSON `3.0`。旧 `1.0/2.0` 工程不自动迁移,加载时会提示格式版本不受支持。 +连续网格使用严格的工程 JSON `4.0`。旧 `1.0/2.0/3.0` 工程不自动迁移,加载时会提示格式版本不受支持。 diff --git a/docs/用户使用/鼠标画线与删线说明.md b/docs/用户使用/鼠标画线与删线说明.md index d9eb37d..b27d2c7 100644 --- a/docs/用户使用/鼠标画线与删线说明.md +++ b/docs/用户使用/鼠标画线与删线说明.md @@ -51,4 +51,4 @@ ## 数据与限制 -每行固定持久化 10 个 `cells`,竖线持久化为相邻行之间的 `verticalConnections`。工程格式为严格 `3.0`,旧 `1.0/2.0` 文件直接拒绝,不写兼容迁移代码。画布像素、鼠标轨迹和悬空自由线段不会写入工程文件。 +每行固定持久化 10 个 `cells`,竖线持久化为相邻行之间的 `verticalConnections`。工程格式为严格 `4.0`,旧 `1.0/2.0/3.0` 文件直接拒绝,不写兼容迁移代码。画布像素、鼠标轨迹和悬空自由线段不会写入工程文件。