# 投煤器布料机远程监控与控制功能实现方案 > 最后更新:2026-03-24(基于当前代码重新审阅) ## 1. 目标 基于当前 `plc_control` 项目,扩展出面向投煤器与布料机的业务化远程监控与控制能力,满足以下目标: - 实时监控投煤器、布料机运行状态 - 支持远程手动启停 - 支持投煤器自动定时运行 - 支持投煤累计触发布料机自动运行 - 支持故障锁定、人工复位、通讯异常冻结 - 支持通过配置适配不同现场,不改代码完成项目复用 --- ## 2. 当前系统能力盘点 ### 2.1 通用平台基础(已有) - OPC UA 数据源接入与自动重连(`connection.rs`) - 节点浏览、批量建点、点位实时订阅(`handler/point.rs`, `handler/source.rs`) - 点位批量写入能力(`connection.rs: write_point_values_batch`) - WebSocket 实时推送(`websocket.rs`) - 前端设备、点位、日志、趋势图基础界面 - 页面配置 `page` 能力 - 进程内事件总线 `event.rs`(control + telemetry 双通道) - HTTP 中间件、鉴权、分页等工具链 ### 2.2 业务模型(已有) - `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 业务逻辑(已有) - 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. 推荐实现思路 继续在现有平台上"增量扩展": - 现有 `unit` / `equipment` / `point` / `event` 底座不动 - 填充 `control/engine.rs` 实现状态机调度 - 新增自动控制和 ack-fault 接口 - `validator.rs` 增加对运行态的检查 - 扩展 `AppEvent` 业务事件,通过 WebSocket 推送运行态变更 - 前端增加业务控制页面 --- ## 5. 命名与模型设计(已落地,确认) ### 5.1 设备分类(`equipment.kind`) - `coal_feeder`:投煤器 - `distributor`:布料机 ### 5.2 点位角色规范(`point.signal_role`) - 状态点:`rem` `run` `flt` `ii` `q` - 控制点:`start_cmd` `stop_cmd` - 可选扩展:`estop` `mode_auto` `mode_manual` `reset_cmd` ### 5.3 Unit 运行态放内存(已确认) 运行态字段已在 `control/runtime.rs:UnitRuntime` 中定义完整,不落库。 服务重启后重新从 `REM/RUN/FLT/Q` 重建运行态,不自动补发命令。 ### 5.4 统一 `event` 表,预留 `alarm` 表(已落地) - `event`:记录"发生了什么",已上线 - `alarm`:记录"需要被告警管理的异常",第二阶段实现 --- ## 6. 控制逻辑设计 ### 6.1 手动控制(已实现,待补充运行态检查) 当前已实现: - `rem == 1` 检查 - `flt == 0` 检查 - 通讯质量检查 - 脉冲写入(300ms) **待补充**:在 `validator.rs` 中增加对 `ControlRuntimeStore` 的检查: - `fault_locked == true` → 拒绝 - `comm_locked == true` → 拒绝 - `manual_ack_required == true` → 拒绝(等待人工确认) ### 6.2 自动控制状态机(待实现) 每个 `unit` 独立维护状态机,在 `control/engine.rs` 中以后台任务驱动。 #### `STOPPED` - 累计停止时间 `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` → 进入 `DISTRIBUTOR_RUNNING` #### `DISTRIBUTOR_RUNNING` - 校验布料机 REM/FLT/质量 - 启动布料机,等待 `RUN == 1` - 计时 `BLTime` - 停止布料机 - 清零 `accumulated_run_sec` - 切回 `STOPPED` 或自动节拍起点 ### 6.3 故障机制(待实现) 任意设备检测到 `FLT == 1`: - 停止该单元自动控制 - `state = FaultLocked`, `fault_locked = true` - 发送并持久化 `FaultLocked` 事件 当 `FLT` 从 `1 → 0` 恢复: - 不自动解锁 - `manual_ack_required = true` - 等待人工调用 `POST /api/control/unit/{id}/ack-fault` ### 6.4 通讯异常机制(待实现) 当 OPC UA 质量位异常或连接中断: - `state = CommLocked`, `comm_locked = true` - 冻结全部控制动作 - 前端按钮灰化 通讯恢复后: - 重新读取 `REM/RUN/FLT` - 重同步运行态 - 不自动补发控制命令 - 持久化恢复事件 - 等待人工操作或下一次自动触发 --- ## 7. 事件体系设计 ### 7.1 继续复用 `src/event.rs` 当前 `AppEvent` 已有: - `SourceCreate/Update/Delete` - `PointCreateBatch/PointDeleteBatch` - `EquipmentStartCommandSent/EquipmentStopCommandSent` - `PointNewValue`(遥测) **待扩展**: ```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`、高频遥测、内部轮询过程 --- ## 8. 后端改造方案 ### 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 完整 | ### 8.2 待新增接口 ``` 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 ### 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 ``` ### 8.4 手动控制补充运行态检查 在 `control/validator.rs: validate_manual_control()` 中增加: ```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` 三元组 → 业务映射 --- ## 9. 前端改造方案 ### 9.1 已有页面(确认现状) | 页面/功能 | 状态 | |-----------|------| | Unit 列表(含 CRUD Modal) | ✅ 基础实现 | | 事件列表 | ✅ 基础实现 | | 设备列表(含 Unit 归属绑定) | ✅ 实现 | | 点位绑定(equipment/signal_role) | ✅ 实现 | | 趋势图 | ✅ 实现 | ### 9.2 待新增页面/功能 **设备控制面板**(单台投煤器/布料机): - REM / RUN / FLT / Q / II 实时显示 - 启动 / 停止按钮(灰化逻辑:comm_locked / fault_locked / manual_ack_required) - 通讯异常、故障锁定提示 - 最近控制事件 **单元总览**(Unit 卡片增强): - 当前状态机状态(STOPPED / RUNNING / DISTRIBUTOR_RUNNING / FAULT_LOCKED / COMM_LOCKED) - 自动/手动切换按钮 - 故障确认按钮(`manual_ack_required == true` 时显示) - 累计运行时间进度 - 投煤器 + 布料机运行状态摘要 **参数在线编辑**: - 现有 Unit Modal 已有表单 - 需补充:保存后通知引擎重新加载(或引擎每次 tick 从 DB 读取配置) **WebSocket 运行态更新**: - 新增 `WsMessage::UnitRuntimeChanged { unit_id, runtime }` 消息类型 - 前端收到后实时更新 Unit 卡片状态,无需轮询 --- ## 10. 分阶段实施建议 ### 第一阶段:补全控制闭环(当前阶段) **目标**:让自动控制可以跑起来,故障/通讯保护机制生效。 待完成工作: 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 卡片增强** - 状态机状态展示、自动/手动切换、故障确认 交付后可验证: - 单台设备手动启停(含故障/通讯拦截) - 单元自动定时启停 - 累计触发布料机运行 - 故障恢复后人工确认才能操作 - 通讯异常冻结后恢复自动同步 ### 第二阶段:增强版 - 单元详情页(运行趋势、事件时间线、参数在线编辑) - 更丰富的趋势图(电流 II、运行状态变化曲线) - 报警规则与 `alarm` 表 - 报警确认与恢复流程 ### 第三阶段:现场适配版 - 导入导出配置 - 项目模板 - 配置校验工具 - 启停联锁自检 - 操作权限控制 --- ## 11. 当前优先落地顺序 从当前代码基础出发,第一阶段建议按下面顺序开发: 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`(数据模型完整) - `src/control/runtime.rs`(运行态结构体完整) - 所有迁移文件(schema 已完整) - `src/handler/point.rs`(保留底层写点,不承载业务控制) --- ## 13. 本次结论 当前项目已经完成了业务底座的建设(数据模型、脉冲写入、手动控制、事件持久化、运行态数据结构),具备了较好的基础。 下一步核心工作集中在: 1. **填充 `control/engine.rs`**——这是最核心的缺口,所有自动控制、故障保护、通讯冻结都需要它来驱动 2. **前端业务控制面板**——让操作员看到并操作设备状态 其余部分(接口、事件、WebSocket)属于连接层,工作量相对可控。