plc_control/docs/投煤器布料机功能实现方案.md

437 lines
15 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 投煤器布料机远程监控与控制功能实现方案
> 最后更新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<Uuid, UnitRuntime>`,已注册到 `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` 更新
- 状态机 tickSTOPPED / 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属于连接层工作量相对可控。