From 884f6ba5f3f0e5b770191ca0948fc90bdc50e7f3 Mon Sep 17 00:00:00 2001 From: caoqianming Date: Wed, 25 Mar 2026 08:27:09 +0800 Subject: [PATCH] docs: update feature spec with current implementation status Co-Authored-By: Claude Sonnet 4.6 --- docs/投煤器布料机功能实现方案.md | 699 ++++++++----------- 1 file changed, 287 insertions(+), 412 deletions(-) diff --git a/docs/投煤器布料机功能实现方案.md b/docs/投煤器布料机功能实现方案.md index 1d38719..c850ac5 100644 --- a/docs/投煤器布料机功能实现方案.md +++ b/docs/投煤器布料机功能实现方案.md @@ -1,4 +1,6 @@ -# 投煤器布料机远程监控与控制功能实现方案 +# 投煤器布料机远程监控与控制功能实现方案 + +> 最后更新:2026-03-24(基于当前代码重新审阅) ## 1. 目标 @@ -11,551 +13,424 @@ - 支持故障锁定、人工复位、通讯异常冻结 - 支持通过配置适配不同现场,不改代码完成项目复用 -## 2. 现有系统能力盘点 +--- -当前项目已经具备较好的通用工业采集平台基础: +## 2. 当前系统能力盘点 -- OPC UA 数据源接入与自动重连 -- 节点浏览、批量建点、点位实时订阅 -- 点位批量写入能力 -- 设备 `equipment` 模型 -- 点位到设备绑定 `equipment_id` -- 点位信号角色字段 `signal_role` -- WebSocket 实时推送 +### 2.1 通用平台基础(已有) + +- OPC UA 数据源接入与自动重连(`connection.rs`) +- 节点浏览、批量建点、点位实时订阅(`handler/point.rs`, `handler/source.rs`) +- 点位批量写入能力(`connection.rs: write_point_values_batch`) +- WebSocket 实时推送(`websocket.rs`) - 前端设备、点位、日志、趋势图基础界面 - 页面配置 `page` 能力 -- 进程内事件总线 `event.rs` +- 进程内事件总线 `event.rs`(control + telemetry 双通道) +- HTTP 中间件、鉴权、分页等工具链 -现状更接近“通用点位监控平台”,还不是“投煤器/布料机业务控制系统”。 +### 2.2 业务模型(已有) -## 3. 与需求的差距 +- `unit` 表(`migrations/20260324090000_add_unit_and_event.sql`) + - 包含 `run_time_sec`, `stop_time_sec`, `acc_time_sec`, `bl_time_sec`, `require_manual_ack_after_fault` +- `event` 表(同上迁移),含 `unit_id`, `equipment_id`, `source_id`, `level`, `payload` +- `equipment.unit_id` FK +- `equipment.kind`,`point.signal_role` +- `ControlUnit`, `EventRecord` 模型(`model.rs`) -需求文档要求的软件能力,当前尚未落地的核心部分如下: +### 2.3 业务逻辑(已有) -- 缺少“控制单元”概念,无法表达一组投煤器对应一组布料机 -- 缺少业务配置模型,无法配置 `RunTime`、`StopTime`、`AccTime`、`BLTime` -- 缺少设备业务类型约束,尚未明确区分投煤器、布料机 -- 缺少设备归属单元字段,尚未形成 `unit -> equipment -> point` 的业务链路 -- 缺少业务信号角色规范,尚未标准化 `REM/RUN/FLT/STA/STP` -- 缺少自动控制状态机 -- 缺少故障锁定与人工确认恢复流程 -- 缺少通讯异常冻结与恢复后重同步机制 -- 缺少脉冲写入封装,当前只有通用批量写点 -- 缺少单元总览、设备详情、控制面板、报警面板等业务界面 -- 缺少统一事件持久化与后续报警模型 +- Unit CRUD 接口:`GET/POST/PUT/DELETE /api/unit`, `GET /api/unit/{id}` +- 设备手动控制接口:`POST /api/control/equipment/{id}/start|stop` + - 内置脉冲写入(写 1 → 等 300ms → 写 0) + - 前置校验:`rem == 1`, `flt == 0`, 通讯质量 good(`control/validator.rs`) +- `AppEvent::EquipmentStartCommandSent/EquipmentStopCommandSent`,自动持久化 `event` 表并推送 WebSocket +- 内存运行态结构体(`control/runtime.rs`) + - `UnitRuntimeState`: Stopped / Running / DistributorRunning / FaultLocked / CommLocked + - `UnitRuntime`:含 `accumulated_run_sec`, `fault_locked`, `comm_locked`, `manual_ack_required` 等全部字段 + - `ControlRuntimeStore`:全局 `HashMap`,已注册到 `AppState` +- `control/validator.rs`:独立的控制前置校验模块 +- `control/engine.rs`:模块骨架已建立,`start()` 函数为空桩 +- 前端:Unit 列表(`units.js`),事件列表(`events.js`),设备 Unit 绑定(`equipment.js`) +- 事件列表接口:`GET /api/event`(支持 `unit_id`, `event_type` 过滤) + +--- + +## 3. 与需求的差距(当前真实状态) + +| 需求项 | 状态 | 说明 | +|--------|------|------| +| Unit / 设备 / 点位数据模型 | ✅ 已完成 | 迁移、模型、CRUD 全部到位 | +| 脉冲写入封装 | ✅ 已完成 | 内置在 `handler/control.rs` | +| 手动控制前置校验 | ✅ 已完成 | `validator.rs` 实现 REM/FLT/quality 检查 | +| 手动启停接口 | ✅ 已完成 | start/stop 接口含脉冲写入和事件持久化 | +| 运行态内存模型 | ✅ 已完成 | `runtime.rs` 数据结构完整,已注入 AppState | +| 事件持久化与查询 | ✅ 已完成 | event 表 + 事件处理 + GET /api/event | +| **自动控制状态机** | ❌ 未实现 | `engine.rs` 是空桩,无任何调度逻辑 | +| **自动控制接口** | ❌ 未实现 | 缺 `start-auto`, `stop-auto` 接口 | +| **故障锁定联动** | ❌ 未实现 | `runtime.fault_locked` 有字段但无联动逻辑 | +| **通讯冻结联动** | ❌ 未实现 | `runtime.comm_locked` 有字段但无联动逻辑 | +| **人工确认解锁接口** | ❌ 未实现 | 缺 `ack-fault` 接口 | +| **手动控制检查运行态** | ❌ 未实现 | `validator.rs` 未读取 `ControlRuntimeStore` | +| **运行态 WebSocket 推送** | ❌ 未实现 | 单元状态变更未推送前端 | +| **运行态查询接口** | ❌ 未实现 | 缺 `GET /api/unit/{id}/runtime` | +| 前端设备控制面板 | ❌ 未实现 | 缺 REM/RUN/FLT 展示和手动启停按钮 | +| 前端单元详情/总览 | ❌ 未实现 | 缺状态机状态展示、自动/手动切换 | +| 前端参数在线编辑 | 🔶 部分 | Unit 编辑 Modal 有表单,但无运行态反馈 | + +--- ## 4. 推荐实现思路 -推荐在现有平台上“增量扩展”,而不是重写: +继续在现有平台上"增量扩展": -- 保留现有 `source/node/point/equipment` 通用底座 -- 新增面向业务控制的配置表和运行态管理 -- 将控制逻辑放在 Rust 服务端,复用当前 OPC UA 连接和写点能力 -- 前端增加业务页面,不破坏现有通用点位管理页面 -- 基于现有 `event.rs` 扩展事件体系,而不是再造一套事件机制 +- 现有 `unit` / `equipment` / `point` / `event` 底座不动 +- 填充 `control/engine.rs` 实现状态机调度 +- 新增自动控制和 ack-fault 接口 +- `validator.rs` 增加对运行态的检查 +- 扩展 `AppEvent` 业务事件,通过 WebSocket 推送运行态变更 +- 前端增加业务控制页面 -这样做的好处是: +--- -- 现有 OPC UA、点位、设备、WebSocket 基础都能继续复用 -- 后续不同现场只需要换设备映射和参数 -- 手动调试仍然可以通过现有点位/设备页面完成 -- 未来做报警、审计、统计时可以直接复用统一事件体系 +## 5. 命名与模型设计(已落地,确认) -## 5. 命名与模型设计 - -### 5.1 命名原则 - -建议区分“代码命名”和“表命名”: - -- 代码模型名保留语义完整性,如 `ControlUnit` -- 数据库表名尽量简洁,如 `unit` - -不建议使用 `group` 作为表名,原因是语义过泛,后续容易与权限分组、界面分组、标签分组等概念冲突。 - -### 5.2 设备分类 - -继续复用 `equipment` 表中的 `kind` 字段,约定: +### 5.1 设备分类(`equipment.kind`) - `coal_feeder`:投煤器 - `distributor`:布料机 -### 5.3 点位角色规范 - -继续复用 `point.signal_role`,建议统一枚举值: +### 5.2 点位角色规范(`point.signal_role`) - 状态点:`rem` `run` `flt` `ii` `q` - 控制点:`start_cmd` `stop_cmd` - 可选扩展:`estop` `mode_auto` `mode_manual` `reset_cmd` -这样每台设备都可以通过“设备 + 点位角色”完成映射,而不是在代码里写死点名。 +### 5.3 Unit 运行态放内存(已确认) -### 5.4 新增 `unit` 表 +运行态字段已在 `control/runtime.rs:UnitRuntime` 中定义完整,不落库。 +服务重启后重新从 `REM/RUN/FLT/Q` 重建运行态,不自动补发命令。 -建议新增 `unit` 表,对应代码模型 `ControlUnit`,表示一个业务控制单元。 +### 5.4 统一 `event` 表,预留 `alarm` 表(已落地) -建议字段: +- `event`:记录"发生了什么",已上线 +- `alarm`:记录"需要被告警管理的异常",第二阶段实现 -- `id` -- `code` -- `name` -- `description` -- `enabled` -- `run_time_sec` -- `stop_time_sec` -- `acc_time_sec` -- `bl_time_sec` -- `require_manual_ack_after_fault` -- `created_at` -- `updated_at` - -### 5.5 设备直接归属 Unit - -当前业务前提下,一台设备只属于一个控制单元,不会跨单元复用,因此不建议单独建立关系表。 - -更合适的方式是直接在 `equipment` 表新增: - -- `unit_id` - -这样模型会更简单: - -- 一个 `unit` 对多台 `equipment` -- 一台 `equipment` 只属于一个 `unit` -- `equipment.kind` 用于区分 `coal_feeder` 和 `distributor` - -如果后续现场出现“一台设备可挂多个单元”或“单元内设备编排顺序复杂”的需求,再演进成关系表会更合适。第一阶段不建议设计过重。 - -### 5.6 Unit 运行态放内存 - -`unit` 运行态不建议优先落数据库,建议由控制引擎保存在内存中。 - -建议维护的内存运行态字段: - -- `state` -- `accumulated_run_sec` -- `current_run_elapsed_sec` -- `current_stop_elapsed_sec` -- `distributor_run_elapsed_sec` -- `fault_locked` -- `comm_locked` -- `manual_ack_required` -- `last_tick_at` - -状态值建议: - -- `stopped` -- `running` -- `distributor_running` -- `fault_locked` -- `comm_locked` - -原因: - -- 这些字段变化频率高,不适合高频写库 -- 服务重启后直接恢复旧控制态并不安全 -- 更合理的方式是重启后重新读取 `REM/RUN/FLT/Q` 并重建运行态 -- 通讯恢复或服务重启后不自动补发控制命令,更符合工业控制安全原则 - -如后续确实需要“断电恢复上下文”或运行分析,再补充轻量级快照能力即可,但不是第一阶段必须项。 - -### 5.7 统一 `event` 表,预留 `alarm` 表 - -建议不要命名为 `control_event`,而是使用统一的 `event` 表。 - -原因: - -- 当前不仅有控制事件,后续还会有配置事件、通讯事件、数据源事件 -- `event` 更适合作为统一审计与业务时间线 -- 现有 `event.rs` 已经是进程内事件总线,命名保持一致更自然 - -建议 `event` 表记录: - -- 手动启动/停止 -- 自动启动/停止 -- 故障锁定 -- 人工解除故障锁定 -- 通讯异常/恢复 -- 数据源创建、更新、删除 -- 控制参数变更 -- 关键状态切换 - -关键字段建议: - -- `id` -- `event_type` -- `level` -- `unit_id` -- `equipment_id` -- `source_id` -- `message` -- `payload` -- `created_at` - -同时建议未来单独设计 `alarm` 表,而不是把报警状态硬塞进 `event` 表。 - -原因: - -- 报警通常有独立生命周期:触发、确认、恢复、清除 -- 报警需要独立字段,如 `severity`、`active`、`acked`、`acked_by`、`acked_at`、`cleared_at` -- 把报警硬塞到 `event` 中会让通用事件表越来越臃肿 - -因此推荐边界是: - -- `event`:记录“发生了什么” -- `alarm`:记录“需要被告警管理的异常” - -第一阶段可以先只落 `event` 表,`alarm` 表先在方案中预留,不急着实现。 +--- ## 6. 控制逻辑设计 -### 6.1 手动控制 +### 6.1 手动控制(已实现,待补充运行态检查) -手动控制前置校验: +当前已实现: +- `rem == 1` 检查 +- `flt == 0` 检查 +- 通讯质量检查 +- 脉冲写入(300ms) -- `REM == 1` -- `FLT == 0` -- 通讯正常 -- 未处于故障锁定 -- 如有急停点,`ESTOP == 0` +**待补充**:在 `validator.rs` 中增加对 `ControlRuntimeStore` 的检查: +- `fault_locked == true` → 拒绝 +- `comm_locked == true` → 拒绝 +- `manual_ack_required == true` → 拒绝(等待人工确认) -控制命令统一走脉冲写入: +### 6.2 自动控制状态机(待实现) -1. 写入 `1` -2. 延时 `200-500ms` -3. 写回 `0` - -服务端需要封装 `pulse_write(point_id, high_ms)`,前端不能直接拼两次写点。 - -### 6.2 自动控制状态机 - -每个 `unit` 独立维护状态机。 +每个 `unit` 独立维护状态机,在 `control/engine.rs` 中以后台任务驱动。 #### `STOPPED` -- 累计停止时间 -- 若 `stop_elapsed >= StopTime`,尝试启动投煤器 -- 成功后切换到 `RUNNING` +- 累计停止时间 `current_stop_elapsed_sec` +- 若 `stop_elapsed >= StopTime` → 校验投煤器 REM/FLT/质量 → 发启动命令 → 切换到 `RUNNING` #### `RUNNING` -- 累计运行时间 +- 累计运行时间 `current_run_elapsed_sec` - `accumulated_run_sec += delta` -- 若 `run_elapsed >= RunTime`,尝试停止投煤器,切换回 `STOPPED` -- 若 `accumulated_run_sec >= AccTime`,进入布料机触发流程 +- 若 `run_elapsed >= RunTime` → 停止投煤器 → 切换回 `STOPPED` +- 若 `accumulated_run_sec >= AccTime` → 进入 `DISTRIBUTOR_RUNNING` #### `DISTRIBUTOR_RUNNING` -- 校验布料机 `REM/FLT/通讯` -- 启动布料机 -- 等待 `RUN == 1` 反馈 +- 校验布料机 REM/FLT/质量 +- 启动布料机,等待 `RUN == 1` - 计时 `BLTime` - 停止布料机 -- 累计时间清零 -- 回到 `STOPPED` 或回到自动节拍起点 +- 清零 `accumulated_run_sec` +- 切回 `STOPPED` 或自动节拍起点 -### 6.3 故障机制 +### 6.3 故障机制(待实现) 任意设备检测到 `FLT == 1`: - - 停止该单元自动控制 -- 标记 `fault_locked = true` -- 禁止再次自动发命令 -- 发送并持久化关键事件 - -当 `FLT` 从 `1 -> 0` 恢复: +- `state = FaultLocked`, `fault_locked = true` +- 发送并持久化 `FaultLocked` 事件 +当 `FLT` 从 `1 → 0` 恢复: - 不自动解锁 -- 标记 `manual_ack_required = true` -- 等待人工在界面点击“解除故障锁定” +- `manual_ack_required = true` +- 等待人工调用 `POST /api/control/unit/{id}/ack-fault` -### 6.4 通讯异常机制 +### 6.4 通讯异常机制(待实现) -当质量位异常或 OPC 连接中断: - -- 标记 `comm_locked = true` +当 OPC UA 质量位异常或连接中断: +- `state = CommLocked`, `comm_locked = true` - 冻结全部控制动作 - 前端按钮灰化 -- 不允许任何自动/手动写入 通讯恢复后: - - 重新读取 `REM/RUN/FLT` - 重同步运行态 - 不自动补发控制命令 -- 发送并持久化恢复事件 +- 持久化恢复事件 - 等待人工操作或下一次自动触发 +--- + ## 7. 事件体系设计 ### 7.1 继续复用 `src/event.rs` -建议不要另起一套业务事件中心,而是在现有 [src/event.rs](D:/projects/plc_control/src/event.rs) 上扩展。 +当前 `AppEvent` 已有: +- `SourceCreate/Update/Delete` +- `PointCreateBatch/PointDeleteBatch` +- `EquipmentStartCommandSent/EquipmentStopCommandSent` +- `PointNewValue`(遥测) -当前它已经承担两类职责: +**待扩展**: -- 控制类内部事件分发 -- 遥测类高频事件分发 - -推荐继续保留这个结构: - -- `AppEvent` 作为统一进程内事件枚举 -- 高频遥测事件继续走内存和 WebSocket -- 低频且有审计价值的事件选择性落库到 `event` 表 +```rust +AutoControlStarted { unit_id } +AutoControlStopped { unit_id } +FaultLocked { unit_id, equipment_id } +FaultAcked { unit_id } +CommLocked { unit_id } +CommRecovered { unit_id } +UnitStateChanged { unit_id, from_state, to_state } +``` ### 7.2 哪些事件适合落库 -适合落库的: +- 适合:所有手动/自动启停、故障、通讯、参数变更、状态切换 +- 不适合:`PointNewValue`、高频遥测、内部轮询过程 -- `SourceCreate` -- `SourceUpdate` -- `SourceDelete` -- 自动控制启动/停止 -- 手动启动/停止命令发送 -- 故障锁定 -- 人工确认恢复 -- 通讯异常/恢复 -- 参数配置变更 -- 单元状态切换 - -不适合直接落库的: - -- `PointNewValue` -- 高频实时遥测 -- 细碎的内部轮询过程 - -### 7.3 推荐扩展方向 - -建议在 `AppEvent` 中逐步增加业务事件,例如: - -- `AutoControlStarted` -- `AutoControlStopped` -- `EquipmentStartCommandSent` -- `EquipmentStopCommandSent` -- `FaultLocked` -- `FaultAcked` -- `CommLocked` -- `CommRecovered` -- `UnitStateChanged` - -这样后续无论是写日志、落库、推送 WebSocket、做报警触发,都可以基于同一个事件入口。 +--- ## 8. 后端改造方案 -### 8.1 新增模块 +### 8.1 已有模块(确认现状) -建议新增: +| 模块 | 文件 | 状态 | +|------|------|------| +| HTTP 控制接口 | `src/handler/control.rs` | ✅ 有 Unit CRUD + start/stop + event list | +| 控制前置校验 | `src/control/validator.rs` | ✅ REM/FLT/质量检查,**待加运行态检查** | +| 内存运行态 | `src/control/runtime.rs` | ✅ 数据结构完整 | +| 自动控制引擎 | `src/control/engine.rs` | ❌ 空桩,待实现 | +| 服务层 Unit | `src/service/` | ✅ CRUD 完整 | -- `src/handler/control.rs` -- `src/service/control.rs` -- `src/control/engine.rs` -- `src/control/runtime.rs` -- `src/control/validator.rs` +### 8.2 待新增接口 -职责划分: - -- `handler`:HTTP 接口 -- `service`:数据库读写 -- `control/engine`:状态机与调度 -- `control/runtime`:内存运行态缓存与同步 -- `control/validator`:控制前置校验 - -### 8.2 新增接口 - -建议新增接口: - -- `GET /api/control/unit` -- `POST /api/control/unit` -- `PUT /api/control/unit/{id}` -- `GET /api/control/unit/{id}` -- `POST /api/control/unit/{id}/start-auto` -- `POST /api/control/unit/{id}/stop-auto` -- `POST /api/control/unit/{id}/ack-fault` -- `POST /api/control/equipment/{id}/start` -- `POST /api/control/equipment/{id}/stop` -- `GET /api/events` +``` +POST /api/control/unit/{id}/start-auto 启动自动控制 +POST /api/control/unit/{id}/stop-auto 停止自动控制 +POST /api/control/unit/{id}/ack-fault 人工确认故障解锁 +GET /api/unit/{id}/runtime 查询运行态(state, elapsed, fault_locked 等) +``` 说明: +- `start/stop-auto` 修改运行态 `auto_enabled`,引擎轮询时读取 +- `ack-fault` 仅在 `manual_ack_required == true` 时允许操作,否则返回 400 -- 设备手动控制必须走业务接口,不建议继续直接暴露给页面做原始点位写入 -- 原 `/api/point/value/batch` 保留给调试或底层工具能力 -- 事件查询接口可以直接面向统一 `event` 表 +### 8.3 控制引擎运行方式(待实现) -### 8.3 控制引擎运行方式 +在 `control/engine.rs: start()` 中实现后台任务: -建议服务启动后增加一个后台任务: +``` +每 500ms 扫描所有 enabled unit + ↓ +从 ControlRuntimeStore 读取运行态 + ↓ +从 connection_manager.get_point_monitor_data_read_guard() 取实时点值 + ↓ +检查质量位 → 更新 comm_locked + ↓ +检查 FLT → 更新 fault_locked + ↓ +驱动状态机 tick + ↓ +有状态变化 → 更新 ControlRuntimeStore → 发 AppEvent → 推 WebSocket +``` -- 每 `500ms` 或 `1s` 扫描所有启用的 `unit` -- 从内存读取运行态缓存 -- 从当前点位监控数据中取 `REM/RUN/FLT/Q` -- 驱动状态机执行 +### 8.4 手动控制补充运行态检查 -控制引擎不要直接查 OPC,应复用当前 `connection_manager` 已维护的实时点值。 +在 `control/validator.rs: validate_manual_control()` 中增加: -### 8.4 关键复用点 +```rust +let runtime = state.control_runtime.get(unit_id).await; +if let Some(runtime) = runtime { + if runtime.fault_locked { + return Err(ApiErr::Forbidden("Unit is fault locked", ...)); + } + if runtime.comm_locked { + return Err(ApiErr::Forbidden("Unit is comm locked", ...)); + } + if runtime.manual_ack_required { + return Err(ApiErr::Forbidden("Fault ack required before control", ...)); + } +} +``` + +### 8.5 关键复用点 可直接复用当前已有能力: +- `connection_manager.get_point_monitor_data_read_guard()` → 读取实时点值 +- `connection_manager.write_point_values_batch()` → 写点(自动控制也走此接口) +- `event_manager.send()` → 统一事件入口 +- `ws_manager.send_to_public()` → 推送运行态变更 +- `unit_id + equipment.kind + point.signal_role` 三元组 → 业务映射 -- `connection_manager` 的点位实时缓存 -- `get_point_monitor_data_read_guard` -- 批量写点能力 -- WebSocket 实时推送 -- `event.rs` 的统一事件入口 -- `unit_id + equipment.kind + point.signal_role` 的业务映射关系 +--- ## 9. 前端改造方案 -建议在现有通用页面之外新增业务页面,避免混杂。 +### 9.1 已有页面(确认现状) -### 9.1 新增页面 +| 页面/功能 | 状态 | +|-----------|------| +| Unit 列表(含 CRUD Modal) | ✅ 基础实现 | +| 事件列表 | ✅ 基础实现 | +| 设备列表(含 Unit 归属绑定) | ✅ 实现 | +| 点位绑定(equipment/signal_role) | ✅ 实现 | +| 趋势图 | ✅ 实现 | -- 单元总览页 -- 单元详情页 -- 设备控制面板 -- 事件记录页 -- 报警页 -- 参数配置页 +### 9.2 待新增页面/功能 -### 9.2 单元总览页内容 - -每个 `unit` 展示: - -- 单元名称 -- 自动/手动状态 -- 当前状态机状态 -- 投煤器运行状态 -- 布料机运行状态 -- 当前累计运行时间 -- 故障锁定状态 -- 通讯状态 - -并提供按钮: - -- 启动自动 -- 停止自动 -- 故障确认/解除锁定 - -### 9.3 设备控制页内容 - -针对单台投煤器/布料机提供: - -- REM/RUN/FLT/Q/II 实时显示 -- 启动按钮 -- 停止按钮 +**设备控制面板**(单台投煤器/布料机): +- REM / RUN / FLT / Q / II 实时显示 +- 启动 / 停止按钮(灰化逻辑:comm_locked / fault_locked / manual_ack_required) - 通讯异常、故障锁定提示 -- 最近事件 +- 最近控制事件 -### 9.4 趋势、事件与报警 +**单元总览**(Unit 卡片增强): +- 当前状态机状态(STOPPED / RUNNING / DISTRIBUTOR_RUNNING / FAULT_LOCKED / COMM_LOCKED) +- 自动/手动切换按钮 +- 故障确认按钮(`manual_ack_required == true` 时显示) +- 累计运行时间进度 +- 投煤器 + 布料机运行状态摘要 -复用已有趋势图能力: +**参数在线编辑**: +- 现有 Unit Modal 已有表单 +- 需补充:保存后通知引擎重新加载(或引擎每次 tick 从 DB 读取配置) -- 电流 `II` 趋势 -- 运行状态变化曲线 -- 事件时间线 +**WebSocket 运行态更新**: +- 新增 `WsMessage::UnitRuntimeChanged { unit_id, runtime }` 消息类型 +- 前端收到后实时更新 Unit 卡片状态,无需轮询 -后续报警页面基于独立 `alarm` 表实现: - -- 当前活动报警 -- 已确认报警 -- 已恢复报警 -- 报警确认操作 +--- ## 10. 分阶段实施建议 -### 第一阶段:最小可用版 +### 第一阶段:补全控制闭环(当前阶段) -目标:先让系统具备业务闭环,但不追求复杂页面。 +**目标**:让自动控制可以跑起来,故障/通讯保护机制生效。 -内容: +待完成工作: -- 新增 `unit` 表 -- 为 `equipment` 增加 `unit_id` -- 约定设备 `kind` 和点位 `signal_role` -- 新增手动控制接口 -- 实现脉冲写入 -- 实现故障锁定与通讯冻结 -- 实现自动控制状态机 -- 基于 `event.rs` 落统一 `event` 表 -- 前端增加一个“控制单元”面板和事件列表 +1. **补充运行态检查到 `validator.rs`** + - 手动控制时检查 `fault_locked`, `comm_locked`, `manual_ack_required` -交付后即可验证: +2. **实现 `control/engine.rs`** + - 后台 500ms 轮询任务 + - 质量检查 → `comm_locked` 更新 + - FLT 检测 → `fault_locked` 更新 + - 状态机 tick(STOPPED / RUNNING / DISTRIBUTOR_RUNNING) -- 单台投煤器启停 -- 单元级自动启停 +3. **新增接口** + - `POST /api/control/unit/{id}/start-auto` + - `POST /api/control/unit/{id}/stop-auto` + - `POST /api/control/unit/{id}/ack-fault` + - `GET /api/unit/{id}/runtime` + +4. **扩展 `AppEvent`** + - `FaultLocked`, `FaultAcked`, `CommLocked`, `CommRecovered`, `UnitStateChanged`, `AutoControlStarted`, `AutoControlStopped` + +5. **WebSocket 运行态推送** + - `WsMessage::UnitRuntimeChanged` + +6. **前端设备控制面板** + - REM/RUN/FLT 展示 + 启停按钮 + 灰化逻辑 + +7. **前端 Unit 卡片增强** + - 状态机状态展示、自动/手动切换、故障确认 + +交付后可验证: +- 单台设备手动启停(含故障/通讯拦截) +- 单元自动定时启停 - 累计触发布料机运行 -- 故障恢复后人工确认 -- 关键操作和状态切换可追溯 +- 故障恢复后人工确认才能操作 +- 通讯异常冻结后恢复自动同步 ### 第二阶段:增强版 -内容: - -- 单元总览页 -- 单元详情页 -- 参数在线编辑 -- 更丰富的趋势图 -- WebSocket 业务事件推送 +- 单元详情页(运行趋势、事件时间线、参数在线编辑) +- 更丰富的趋势图(电流 II、运行状态变化曲线) - 报警规则与 `alarm` 表 - 报警确认与恢复流程 ### 第三阶段:现场适配版 -内容: - - 导入导出配置 - 项目模板 - 配置校验工具 - 启停联锁自检 - 操作权限控制 -## 11. 建议优先落地顺序 +--- -从当前代码基础出发,建议按下面顺序开发: +## 11. 当前优先落地顺序 -1. 补齐业务数据模型和数据库迁移 -2. 新增 `unit` 表并为 `equipment` 增加 `unit_id` -3. 规范 `equipment.kind` 与 `point.signal_role` -4. 实现服务端脉冲写入能力 -5. 实现手动控制接口 -6. 实现 `unit` 自动控制状态机 -7. 扩展 `event.rs` 并实现统一 `event` 表持久化 -8. 实现故障锁定、通讯冻结、人工确认 -9. 增加前端业务页面 -10. 第二阶段再引入独立 `alarm` 表 +从当前代码基础出发,第一阶段建议按下面顺序开发: + +1. `validator.rs` 补充运行态检查 +2. `engine.rs` 实现质量检查与 `comm_locked` 更新 +3. `engine.rs` 实现 FLT 检测与 `fault_locked` 更新 +4. `engine.rs` 实现状态机主循环 +5. `handler/control.rs` 新增 `start-auto`, `stop-auto`, `ack-fault` +6. `handler/control.rs` 新增 `GET /api/unit/{id}/runtime` +7. 扩展 `AppEvent` 业务事件类型并落库 +8. `websocket.rs` 新增 `UnitRuntimeChanged` 消息 +9. 前端设备控制面板 +10. 前端 Unit 卡片增强 +11. 第二阶段再引入独立 `alarm` 表 + +--- ## 12. 对当前代码的具体落点 -基于现有代码,建议主要改动点如下: +| 文件 | 改动内容 | +|------|---------| +| `src/control/engine.rs` | 填充后台轮询任务,实现状态机 tick | +| `src/control/validator.rs` | 增加运行态检查(fault/comm/ack) | +| `src/handler/control.rs` | 新增 start-auto, stop-auto, ack-fault, runtime 接口 | +| `src/event.rs` | 扩展 `AppEvent` 业务事件枚举 | +| `src/websocket.rs` | 新增 `WsMessage::UnitRuntimeChanged` | +| `web/js/units.js` | Unit 卡片增加状态机状态、auto 切换、fault ack 按钮 | +| `web/js/equipment.js` | 增加设备控制面板(REM/RUN/FLT + 启停) | +| `web/js/app.js` | 绑定新按钮事件,处理 WebSocket 运行态消息 | -- [src/model.rs](D:/projects/plc_control/src/model.rs) - - 增加 `ControlUnit` 模型 - - 为 `Equipment` 增加 `unit_id` - - 后续增加 `EventRecord`、`AlarmRecord` 模型 -- [src/event.rs](D:/projects/plc_control/src/event.rs) - - 扩展 `AppEvent` 业务事件类型 - - 增加低频关键事件的持久化逻辑 -- [src/main.rs](D:/projects/plc_control/src/main.rs) - - 注册控制相关路由 - - 启动控制引擎后台任务 -- [src/handler/point.rs](D:/projects/plc_control/src/handler/point.rs) - - 保留底层写点接口,不直接承载业务控制 -- [src/handler/equipment.rs](D:/projects/plc_control/src/handler/equipment.rs) - - 继续作为设备基础资料管理 -- [web/index.html](D:/projects/plc_control/web/index.html) - - 增加业务控制页面入口或独立面板 -- [web/js/app.js](D:/projects/plc_control/web/js/app.js) - - 增加控制单元页面事件绑定 +不需要改动的: +- `src/model.rs`(数据模型完整) +- `src/control/runtime.rs`(运行态结构体完整) +- 所有迁移文件(schema 已完整) +- `src/handler/point.rs`(保留底层写点,不承载业务控制) + +--- ## 13. 本次结论 -当前项目不需要推倒重来,可以直接演进成投煤器与布料机远程监控控制系统。 +当前项目已经完成了业务底座的建设(数据模型、脉冲写入、手动控制、事件持久化、运行态数据结构),具备了较好的基础。 -最合理的路径是: +下一步核心工作集中在: -- 以现有 OPC UA 与点位平台为底座 -- 数据库使用简洁表名 `unit`、`event` -- 代码层保留语义化命名,如 `ControlUnit`、`AppEvent` -- 在 `equipment` 上直接增加 `unit_id` -- 在服务端以内存运行态实现状态机和脉冲控制 -- 在现有 [src/event.rs](D:/projects/plc_control/src/event.rs) 上扩展统一事件体系 -- 第一阶段先做统一 `event`,第二阶段再拆分独立 `alarm` +1. **填充 `control/engine.rs`**——这是最核心的缺口,所有自动控制、故障保护、通讯冻结都需要它来驱动 +2. **前端业务控制面板**——让操作员看到并操作设备状态 -如果进入下一步开发,建议先做“第一阶段最小可用版”。 +其余部分(接口、事件、WebSocket)属于连接层,工作量相对可控。