# 投煤器布料机远程监控与控制功能实现方案 ## 1. 目标 基于当前 `plc_control` 项目,扩展出面向投煤器与布料机的业务化远程监控与控制能力,满足以下目标: - 实时监控投煤器、布料机运行状态 - 支持远程手动启停 - 支持投煤器自动定时运行 - 支持投煤累计触发布料机自动运行 - 支持故障锁定、人工复位、通讯异常冻结 - 支持通过配置适配不同现场,不改代码完成项目复用 ## 2. 现有系统能力盘点 当前项目已经具备较好的通用工业采集平台基础: - OPC UA 数据源接入与自动重连 - 节点浏览、批量建点、点位实时订阅 - 点位批量写入能力 - 设备 `equipment` 模型 - 点位到设备绑定 `equipment_id` - 点位信号角色字段 `signal_role` - WebSocket 实时推送 - 前端设备、点位、日志、趋势图基础界面 - 页面配置 `page` 能力 - 进程内事件总线 `event.rs` 现状更接近“通用点位监控平台”,还不是“投煤器/布料机业务控制系统”。 ## 3. 与需求的差距 需求文档要求的软件能力,当前尚未落地的核心部分如下: - 缺少“控制单元”概念,无法表达一组投煤器对应一组布料机 - 缺少业务配置模型,无法配置 `RunTime`、`StopTime`、`AccTime`、`BLTime` - 缺少设备业务类型约束,尚未明确区分投煤器、布料机 - 缺少设备归属单元字段,尚未形成 `unit -> equipment -> point` 的业务链路 - 缺少业务信号角色规范,尚未标准化 `REM/RUN/FLT/STA/STP` - 缺少自动控制状态机 - 缺少故障锁定与人工确认恢复流程 - 缺少通讯异常冻结与恢复后重同步机制 - 缺少脉冲写入封装,当前只有通用批量写点 - 缺少单元总览、设备详情、控制面板、报警面板等业务界面 - 缺少统一事件持久化与后续报警模型 ## 4. 推荐实现思路 推荐在现有平台上“增量扩展”,而不是重写: - 保留现有 `source/node/point/equipment` 通用底座 - 新增面向业务控制的配置表和运行态管理 - 将控制逻辑放在 Rust 服务端,复用当前 OPC UA 连接和写点能力 - 前端增加业务页面,不破坏现有通用点位管理页面 - 基于现有 `event.rs` 扩展事件体系,而不是再造一套事件机制 这样做的好处是: - 现有 OPC UA、点位、设备、WebSocket 基础都能继续复用 - 后续不同现场只需要换设备映射和参数 - 手动调试仍然可以通过现有点位/设备页面完成 - 未来做报警、审计、统计时可以直接复用统一事件体系 ## 5. 命名与模型设计 ### 5.1 命名原则 建议区分“代码命名”和“表命名”: - 代码模型名保留语义完整性,如 `ControlUnit` - 数据库表名尽量简洁,如 `unit` 不建议使用 `group` 作为表名,原因是语义过泛,后续容易与权限分组、界面分组、标签分组等概念冲突。 ### 5.2 设备分类 继续复用 `equipment` 表中的 `kind` 字段,约定: - `coal_feeder`:投煤器 - `distributor`:布料机 ### 5.3 点位角色规范 继续复用 `point.signal_role`,建议统一枚举值: - 状态点:`rem` `run` `flt` `ii` `q` - 控制点:`start_cmd` `stop_cmd` - 可选扩展:`estop` `mode_auto` `mode_manual` `reset_cmd` 这样每台设备都可以通过“设备 + 点位角色”完成映射,而不是在代码里写死点名。 ### 5.4 新增 `unit` 表 建议新增 `unit` 表,对应代码模型 `ControlUnit`,表示一个业务控制单元。 建议字段: - `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 手动控制 手动控制前置校验: - `REM == 1` - `FLT == 0` - 通讯正常 - 未处于故障锁定 - 如有急停点,`ESTOP == 0` 控制命令统一走脉冲写入: 1. 写入 `1` 2. 延时 `200-500ms` 3. 写回 `0` 服务端需要封装 `pulse_write(point_id, high_ms)`,前端不能直接拼两次写点。 ### 6.2 自动控制状态机 每个 `unit` 独立维护状态机。 #### `STOPPED` - 累计停止时间 - 若 `stop_elapsed >= StopTime`,尝试启动投煤器 - 成功后切换到 `RUNNING` #### `RUNNING` - 累计运行时间 - `accumulated_run_sec += delta` - 若 `run_elapsed >= RunTime`,尝试停止投煤器,切换回 `STOPPED` - 若 `accumulated_run_sec >= AccTime`,进入布料机触发流程 #### `DISTRIBUTOR_RUNNING` - 校验布料机 `REM/FLT/通讯` - 启动布料机 - 等待 `RUN == 1` 反馈 - 计时 `BLTime` - 停止布料机 - 累计时间清零 - 回到 `STOPPED` 或回到自动节拍起点 ### 6.3 故障机制 任意设备检测到 `FLT == 1`: - 停止该单元自动控制 - 标记 `fault_locked = true` - 禁止再次自动发命令 - 发送并持久化关键事件 当 `FLT` 从 `1 -> 0` 恢复: - 不自动解锁 - 标记 `manual_ack_required = true` - 等待人工在界面点击“解除故障锁定” ### 6.4 通讯异常机制 当质量位异常或 OPC 连接中断: - 标记 `comm_locked = true` - 冻结全部控制动作 - 前端按钮灰化 - 不允许任何自动/手动写入 通讯恢复后: - 重新读取 `REM/RUN/FLT` - 重同步运行态 - 不自动补发控制命令 - 发送并持久化恢复事件 - 等待人工操作或下一次自动触发 ## 7. 事件体系设计 ### 7.1 继续复用 `src/event.rs` 建议不要另起一套业务事件中心,而是在现有 [src/event.rs](D:/projects/plc_control/src/event.rs) 上扩展。 当前它已经承担两类职责: - 控制类内部事件分发 - 遥测类高频事件分发 推荐继续保留这个结构: - `AppEvent` 作为统一进程内事件枚举 - 高频遥测事件继续走内存和 WebSocket - 低频且有审计价值的事件选择性落库到 `event` 表 ### 7.2 哪些事件适合落库 适合落库的: - `SourceCreate` - `SourceUpdate` - `SourceDelete` - 自动控制启动/停止 - 手动启动/停止命令发送 - 故障锁定 - 人工确认恢复 - 通讯异常/恢复 - 参数配置变更 - 单元状态切换 不适合直接落库的: - `PointNewValue` - 高频实时遥测 - 细碎的内部轮询过程 ### 7.3 推荐扩展方向 建议在 `AppEvent` 中逐步增加业务事件,例如: - `AutoControlStarted` - `AutoControlStopped` - `EquipmentStartCommandSent` - `EquipmentStopCommandSent` - `FaultLocked` - `FaultAcked` - `CommLocked` - `CommRecovered` - `UnitStateChanged` 这样后续无论是写日志、落库、推送 WebSocket、做报警触发,都可以基于同一个事件入口。 ## 8. 后端改造方案 ### 8.1 新增模块 建议新增: - `src/handler/control.rs` - `src/service/control.rs` - `src/control/engine.rs` - `src/control/runtime.rs` - `src/control/validator.rs` 职责划分: - `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` 说明: - 设备手动控制必须走业务接口,不建议继续直接暴露给页面做原始点位写入 - 原 `/api/point/value/batch` 保留给调试或底层工具能力 - 事件查询接口可以直接面向统一 `event` 表 ### 8.3 控制引擎运行方式 建议服务启动后增加一个后台任务: - 每 `500ms` 或 `1s` 扫描所有启用的 `unit` - 从内存读取运行态缓存 - 从当前点位监控数据中取 `REM/RUN/FLT/Q` - 驱动状态机执行 控制引擎不要直接查 OPC,应复用当前 `connection_manager` 已维护的实时点值。 ### 8.4 关键复用点 可直接复用当前已有能力: - `connection_manager` 的点位实时缓存 - `get_point_monitor_data_read_guard` - 批量写点能力 - WebSocket 实时推送 - `event.rs` 的统一事件入口 - `unit_id + equipment.kind + point.signal_role` 的业务映射关系 ## 9. 前端改造方案 建议在现有通用页面之外新增业务页面,避免混杂。 ### 9.1 新增页面 - 单元总览页 - 单元详情页 - 设备控制面板 - 事件记录页 - 报警页 - 参数配置页 ### 9.2 单元总览页内容 每个 `unit` 展示: - 单元名称 - 自动/手动状态 - 当前状态机状态 - 投煤器运行状态 - 布料机运行状态 - 当前累计运行时间 - 故障锁定状态 - 通讯状态 并提供按钮: - 启动自动 - 停止自动 - 故障确认/解除锁定 ### 9.3 设备控制页内容 针对单台投煤器/布料机提供: - REM/RUN/FLT/Q/II 实时显示 - 启动按钮 - 停止按钮 - 通讯异常、故障锁定提示 - 最近事件 ### 9.4 趋势、事件与报警 复用已有趋势图能力: - 电流 `II` 趋势 - 运行状态变化曲线 - 事件时间线 后续报警页面基于独立 `alarm` 表实现: - 当前活动报警 - 已确认报警 - 已恢复报警 - 报警确认操作 ## 10. 分阶段实施建议 ### 第一阶段:最小可用版 目标:先让系统具备业务闭环,但不追求复杂页面。 内容: - 新增 `unit` 表 - 为 `equipment` 增加 `unit_id` - 约定设备 `kind` 和点位 `signal_role` - 新增手动控制接口 - 实现脉冲写入 - 实现故障锁定与通讯冻结 - 实现自动控制状态机 - 基于 `event.rs` 落统一 `event` 表 - 前端增加一个“控制单元”面板和事件列表 交付后即可验证: - 单台投煤器启停 - 单元级自动启停 - 累计触发布料机运行 - 故障恢复后人工确认 - 关键操作和状态切换可追溯 ### 第二阶段:增强版 内容: - 单元总览页 - 单元详情页 - 参数在线编辑 - 更丰富的趋势图 - WebSocket 业务事件推送 - 报警规则与 `alarm` 表 - 报警确认与恢复流程 ### 第三阶段:现场适配版 内容: - 导入导出配置 - 项目模板 - 配置校验工具 - 启停联锁自检 - 操作权限控制 ## 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` 表 ## 12. 对当前代码的具体落点 基于现有代码,建议主要改动点如下: - [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) - 增加控制单元页面事件绑定 ## 13. 本次结论 当前项目不需要推倒重来,可以直接演进成投煤器与布料机远程监控控制系统。 最合理的路径是: - 以现有 OPC UA 与点位平台为底座 - 数据库使用简洁表名 `unit`、`event` - 代码层保留语义化命名,如 `ControlUnit`、`AppEvent` - 在 `equipment` 上直接增加 `unit_id` - 在服务端以内存运行态实现状态机和脉冲控制 - 在现有 [src/event.rs](D:/projects/plc_control/src/event.rs) 上扩展统一事件体系 - 第一阶段先做统一 `event`,第二阶段再拆分独立 `alarm` 如果进入下一步开发,建议先做“第一阶段最小可用版”。