Add operation-system engine design spec
Spec covers station/segment/step/interlock domain model, segment state machine (Idle..ManualAckRequired), action templates including persistent commands, resource lease registry, ops.* event taxonomy, and the AppEvent WebSocket envelope. Stage plan includes P-1 core cleanup before ops work begins. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
2a6dde9e0e
commit
3467f203ca
|
|
@ -0,0 +1,631 @@
|
|||
# 运转系统顺控引擎设计
|
||||
|
||||
日期:2026-05-18
|
||||
|
||||
参考来源:
|
||||
- `运转系统逻辑说明.doc`(说明书 14 章)
|
||||
- `docs/运转系统实现方案.md`(高层方案)
|
||||
- `docs/superpowers/specs/2026-04-14-dual-app-shared-core-design.md`(双应用共享核心架构)
|
||||
- 现有 `crates/app_feeder_distributor` 实现作为工程参考
|
||||
|
||||
## 1. 目标
|
||||
|
||||
在已经搭好的 `crates/app_operation_system` 骨架内,落地说明书中规定的整线自动控制能力:
|
||||
|
||||
- 覆盖 8 个业务子系统:回车线、前端码车道、机械臂、摆渡车、1 号干燥/焙烧窑、2 号干燥/焙烧窑、窑尾下摆渡车、卸砖机位。
|
||||
- 引擎语义遵循说明书第 1.4 与 13 章:"顺序控制 + 联锁保护 + 检测信号闭环确认 + 异常停留人工恢复"。
|
||||
- 双窑线(1 号 / 2 号)采用同一套段模板,仅通过参数差异化,不写两套代码。
|
||||
- 复用 `plc_platform_core` 的接入层(OPC UA / 点位 / 设备 / 事件 / WebSocket / 日志)。
|
||||
- 不引入 `app_feeder_distributor` 的 `unit + run_time/stop_time/acc_time/bl_time` 业务模型。
|
||||
|
||||
非目标(首期):
|
||||
|
||||
- 不做规则引擎或 DSL,只支持固定 `rule_kind` 联锁判定。
|
||||
- 不做高级排程(最大化吞吐、动态优化),只做基于空位/资源占用的放行决策。
|
||||
- 不做权限/审计/历史回放。
|
||||
|
||||
## 2. 设计结论
|
||||
|
||||
| 决策 | 选择 | 原因 |
|
||||
| --- | --- | --- |
|
||||
| 业务模型 | **station + segment + step + interlock** | 说明书是工位驱动的整线顺控,不是节拍式设备启停 |
|
||||
| `unit` 表 | **不复用** | 语义不匹配;ops 自己建 `process_segment` |
|
||||
| 引擎调度单位 | **段(segment)** | 每个 enabled segment 一个 tokio task,对齐 feeder 引擎结构 |
|
||||
| 双窑线参数化 | **同一段模板 + line_code 区分实例** | 对齐说明书第 11 章 |
|
||||
| 联锁配置 | **数据库表 + 固定 rule_kind 枚举** | 首期不引入表达式语言 |
|
||||
| WebSocket 消息扩展 | **core 保持通用通道,ops 使用业务 payload 分支** | 避免 `plc_platform_core` 反向依赖 ops 领域类型;前端仍只连一处 |
|
||||
| 报警 | **走 `event` 表 + `subject_type/subject_id` + `level=warn/error`** | 复用现有事件表,同时支持按段 / 工位查询 |
|
||||
| 公共资源互斥 | **app 内部命名锁注册表 + 租约/恢复策略** | 摆渡车 / 机械臂 / 卸砖机位等共享资源,防止 task 异常退出后长期占锁 |
|
||||
|
||||
## 3. 不沿用 feeder 模型的理由
|
||||
|
||||
`ControlUnit` 当前字段是 `run_time_sec / stop_time_sec / acc_time_sec / bl_time_sec`,语义是"运行 N 秒 → 停 M 秒 → 累计 K 秒后启动布料机 → 布料 L 秒"。
|
||||
|
||||
运转系统的核心动作完全不是这种节拍:
|
||||
- 说明书 8.2 要求"码车位到车确认 → 输送机构停止",是检测信号驱动,不是定时。
|
||||
- 说明书 10.1 要求"开门 → 门开到位确认 → 顶车 → 前位确认 → 顶车后退 → 后位确认 → 关门 → 门关到位确认",是 8 步串行带闭环。
|
||||
- 说明书 13 章明确要求"动作完成不得仅靠时间,必须结合限位、检测或反馈信号"。
|
||||
|
||||
因此引擎需要换语义:**段(segment)状态机 + 步骤(step)顺序 + 每步等待闭环信号**。
|
||||
|
||||
## 4. 领域模型
|
||||
|
||||
### 4.1 实体一览
|
||||
|
||||
```
|
||||
source ──┐
|
||||
│
|
||||
point ───┼─→ equipment
|
||||
│
|
||||
├─→ station_signal ──→ station ──┐
|
||||
│ │
|
||||
└──────────────→ segment_step ──→ process_segment ──→ segment_runtime
|
||||
│ │
|
||||
│ ├──→ segment_interlock
|
||||
│ └──→ segment_resource
|
||||
│
|
||||
└──→ action_kind (枚举)
|
||||
```
|
||||
|
||||
`source / point / equipment` 沿用平台层定义,不改动。
|
||||
|
||||
信号边界:
|
||||
|
||||
- `point.signal_role` 是设备信号角色,例如 `rem / flt / home / run / start_cmd / stop_cmd / open_cmd / close_cmd`。
|
||||
- `station_signal.signal_role` 是工位信号角色,例如 `presence / vacancy / arrived / allow_in / done / fault`。
|
||||
- 同一个 `point` 可以同时被设备角色和工位角色引用,但两者语义分开维护。
|
||||
- `vacancy` 可由独立点位绑定,也可由 `presence = false` 推导。首期通过 `station_signal.derived_from_role` 表达推导关系,避免现场必须额外提供空位点。
|
||||
|
||||
### 4.2 新增对象
|
||||
|
||||
#### 4.2.1 `station`(工位)
|
||||
|
||||
表示流程中的一个位置或交接位。
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| `id` | UUID | |
|
||||
| `code` | TEXT UNIQUE | 例 `ST-DRY1-IN` |
|
||||
| `name` | TEXT | 例 "1 号干燥窑进口位" |
|
||||
| `line_code` | TEXT NULL | 例 `KILN_1` / `KILN_2` / `COMMON` |
|
||||
| `segment_code` | TEXT NULL | 用于分组(前端码车 / 双窑线 / 窑尾) |
|
||||
| `station_type` | TEXT | `load / dry_in / dry_step / dry_out / fire_in / fire_step / fire_out / transfer / unload / return` |
|
||||
| `enabled` | BOOL | |
|
||||
| `description` | TEXT NULL | |
|
||||
|
||||
#### 4.2.2 `station_signal`(工位 ↔ 信号绑定)
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| `id` | UUID | |
|
||||
| `station_id` | UUID FK | |
|
||||
| `signal_role` | TEXT | `presence / vacancy / arrived / allow_in / done / fault` |
|
||||
| `point_id` | UUID FK | 绑定到具体点位 |
|
||||
| `derived_from_role` | TEXT NULL | 例 `presence`,表示由同工位其他角色反向推导 |
|
||||
| `invert_value` | BOOL | 推导或读取时是否取反,默认 false |
|
||||
| UNIQUE | (`station_id`, `signal_role`) | |
|
||||
|
||||
#### 4.2.3 `process_segment`(流程段)
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| `id` | UUID | |
|
||||
| `code` | TEXT UNIQUE | 例 `SEG-DRY1-INFEED` |
|
||||
| `name` | TEXT | |
|
||||
| `segment_type` | TEXT | `front_load / robot / front_release / front_transfer / kiln_infeed / kiln_step / kiln_outfeed / tail_transfer / tail_step / unload / return` |
|
||||
| `line_code` | TEXT NULL | `KILN_1` / `KILN_2` / `COMMON` |
|
||||
| `priority` | INT | 公共资源冲突时使用 |
|
||||
| `enabled` | BOOL | |
|
||||
| `mode` | TEXT | `auto / remote_manual / local_manual / disabled` |
|
||||
| `require_manual_ack_after_fault` | BOOL | 故障解除后是否需要人工确认,默认 true |
|
||||
| `description` | TEXT NULL | |
|
||||
|
||||
模式语义:
|
||||
|
||||
- `local_manual`:现场就地优先,软件不推进自动顺控;自动运行中检测到任一相关设备 `rem=false` 时,停止当前自动段并进入人工恢复路径。
|
||||
- `remote_manual`:允许通过软件发单步 / 单设备命令,但仍必须执行设备故障、通信质量、安全链和关键门位联锁。
|
||||
- `auto`:允许 supervisor 自动推进段状态机。
|
||||
- `disabled`:段任务不启动;已运行任务在下一次配置重载后退出。
|
||||
|
||||
#### 4.2.4 `segment_step`(段步骤)
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| `id` | UUID | |
|
||||
| `segment_id` | UUID FK | |
|
||||
| `step_no` | INT | 序号 |
|
||||
| `step_code` | TEXT | 步骤代号 |
|
||||
| `action_kind` | TEXT | 见下方动作模板表 |
|
||||
| `target_equipment_id` | UUID NULL FK | 例如顶车机 |
|
||||
| `target_station_id` | UUID NULL FK | 例如目标摆渡位 |
|
||||
| `confirm_signal_role` | TEXT NULL | 等待哪个信号角色为真 |
|
||||
| `confirm_point_id` | UUID NULL FK | 直接指定确认点位(覆盖 role) |
|
||||
| `expected_value` | BOOL | 信号到位的期望值(默认 true) |
|
||||
| `timeout_ms` | INT | 超时即报警转 Faulted |
|
||||
| `command_role` | TEXT NULL | 设备命令角色,例 `start_cmd / open_cmd / forward_cmd` |
|
||||
| `stop_command_role` | TEXT NULL | 到位或异常时需要发出的停止命令角色,例 `stop_cmd` |
|
||||
| `pulse_ms` | INT NULL | 脉冲命令宽度;为空时按 action 默认值 |
|
||||
| `hold_until_confirm` | BOOL | true 表示命令保持到确认信号或故障;false 表示脉冲后等待 |
|
||||
| `cancel_on_fault` | BOOL | 故障 / 模式切换 / 通信异常时是否执行停止命令,默认 true |
|
||||
| `next_step_no_on_success` | INT NULL | 成功后跳转;为空表示顺序进入下一 step |
|
||||
| `next_step_no_on_failure` | INT NULL | 失败后跳转;首期通常为空并进入 Faulted |
|
||||
| `on_timeout` | TEXT | `fault / retry / block`,首期默认 `fault` |
|
||||
| `description` | TEXT NULL | |
|
||||
| UNIQUE | (`segment_id`, `step_no`) | |
|
||||
|
||||
`action_kind` 枚举(首期):
|
||||
|
||||
| 值 | 含义 |
|
||||
| --- | --- |
|
||||
| `open_door` | 开门:向门机 `open_cmd` 发脉冲 |
|
||||
| `close_door` | 关门 |
|
||||
| `push_forward` | 顶车机前进 |
|
||||
| `push_retract` | 顶车机后退复位 |
|
||||
| `pull_run` | 拉引机拉车 |
|
||||
| `pull_retract` | 拉引机复位 |
|
||||
| `transfer_move_to` | 摆渡车移动到目标工位 |
|
||||
| `step_once` | 节拍步进机执行一步 |
|
||||
| `robot_permit` | 允许机械臂自动作业 |
|
||||
| `robot_release` | 允许码车道放车 |
|
||||
| `wait_signal` | 不发命令,仅等待 `confirm_*` |
|
||||
| `pulse_cmd` | 通用脉冲命令(fallback) |
|
||||
|
||||
动作执行策略:
|
||||
|
||||
- 对 `open_door / close_door / robot_permit` 等短命令,默认 `pulse_ms=300`,命令发出后等待确认信号。
|
||||
- 对输送、顶车、拉引、步进等持续动作,默认 `hold_until_confirm=true`,到位后执行 `stop_command_role`。
|
||||
- 对故障、急停、通信质量异常、自动切就地等中断场景,若 `cancel_on_fault=true`,先发停止 / 复位命令,再进入 `Faulted` 或 `ManualAckRequired`。
|
||||
|
||||
#### 4.2.5 `segment_interlock`(段联锁)
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| `id` | UUID | |
|
||||
| `segment_id` | UUID FK | |
|
||||
| `applies_to` | TEXT | `start_allow / start_deny / run_halt` |
|
||||
| `rule_kind` | TEXT | 见下方 |
|
||||
| `point_id` | UUID NULL FK | |
|
||||
| `station_id` | UUID NULL FK | |
|
||||
| `equipment_id` | UUID NULL FK | |
|
||||
| `expected_value` | BOOL NULL | |
|
||||
| `description` | TEXT NULL | |
|
||||
|
||||
`rule_kind` 枚举(首期):
|
||||
|
||||
- `point_eq` —— 指定 point 的值等于 `expected_value`
|
||||
- `station_vacant` —— 工位空(绑定的 `vacancy` 信号 = true 且 `presence` = false)
|
||||
- `station_occupied` —— 工位有车
|
||||
- `equipment_origin` —— 设备在原位(角色 `home`)
|
||||
- `equipment_no_fault` —— 设备无故障(`flt` = false)
|
||||
- `equipment_remote` —— 设备远程(`rem` = true)
|
||||
- `safety_chain_ok` —— 安全链路正常
|
||||
|
||||
未来可扩展 `expression` 类型,但首期不引入。
|
||||
|
||||
#### 4.2.6 `segment_runtime`(段运行态,内存)
|
||||
|
||||
不落库(与 feeder `UnitRuntime` 一致,重启重置):
|
||||
|
||||
```rust
|
||||
pub enum SegmentState {
|
||||
Idle,
|
||||
Checking,
|
||||
Executing,
|
||||
Confirming,
|
||||
Resetting,
|
||||
Completed,
|
||||
Blocked,
|
||||
Faulted,
|
||||
ManualAckRequired,
|
||||
}
|
||||
|
||||
pub struct SegmentRuntime {
|
||||
pub segment_id: Uuid,
|
||||
pub state: SegmentState,
|
||||
pub auto_enabled: bool,
|
||||
pub current_step_no: Option<i32>,
|
||||
pub step_started_at: Option<DateTime<Utc>>,
|
||||
pub last_completed_at: Option<DateTime<Utc>>,
|
||||
pub blocked_reason: Option<String>,
|
||||
pub fault_message: Option<String>,
|
||||
pub manual_ack_required: bool,
|
||||
pub comm_locked: bool,
|
||||
pub rem_local: bool,
|
||||
pub held_resources: Vec<String>,
|
||||
}
|
||||
```
|
||||
|
||||
#### 4.2.7 `segment_resource`(段资源声明)
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| `segment_id` | UUID FK | |
|
||||
| `resource_key` | TEXT | 例 `transfer_front / transfer_tail / robot_arm / unload_position / return_line` |
|
||||
| UNIQUE | (`segment_id`, `resource_key`) | |
|
||||
|
||||
#### 4.2.8 `event` 表归因扩展
|
||||
|
||||
现有 `event` 表保留 `unit_id / equipment_id / source_id`,为了支持 ops 按段、工位检索,新增通用归因字段:
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| `subject_type` | TEXT NULL | `segment / station / equipment / source / platform` |
|
||||
| `subject_id` | UUID NULL | 对应业务对象 ID |
|
||||
|
||||
ops 事件写入规则:
|
||||
|
||||
- 段级事件:`subject_type='segment'`,`subject_id=segment_id`。
|
||||
- 工位状态事件:`subject_type='station'`,`subject_id=station_id`。
|
||||
- 设备动作事件:优先保留 `equipment_id`,同时可按上下文设置 `subject_type='segment'`。
|
||||
|
||||
### 4.3 双窑线参数化
|
||||
|
||||
不写两套硬编码逻辑。1 号与 2 号窑线的差异由:
|
||||
|
||||
- `process_segment.line_code`(`KILN_1` / `KILN_2`)
|
||||
- `segment_step.target_equipment_id` 与 `target_station_id`(指向各自的门机、顶车机、工位)
|
||||
- `segment_interlock.point_id` / `station_id`(指向各自工位的检测点)
|
||||
|
||||
承载。引擎读到的就是统一的 step 列表,与窑线无关。
|
||||
|
||||
## 5. 顺控引擎设计
|
||||
|
||||
### 5.1 结构(与 feeder 对齐)
|
||||
|
||||
```
|
||||
crates/app_operation_system/src/
|
||||
app.rs // AppState 接入 segment_runtime + event_manager + resource_registry
|
||||
router.rs
|
||||
event.rs // AppEvent(ops.*)
|
||||
control/
|
||||
mod.rs
|
||||
engine.rs // supervisor + per-segment task
|
||||
runtime.rs // SegmentRuntime / SegmentRuntimeStore
|
||||
state.rs // SegmentState enum
|
||||
step_executor.rs // 按 action_kind 调度
|
||||
interlock.rs // 通用允许/禁止/停机判定
|
||||
resource.rs // 摆渡车 / 机械臂 / 卸砖位 互斥
|
||||
simulate.rs // 开发态信号回灌
|
||||
handler/
|
||||
doc.rs (已存在)
|
||||
station.rs // CRUD + 信号绑定
|
||||
segment.rs // CRUD + step / interlock 配置
|
||||
control.rs // 段启停 / 手动动作 / 故障确认
|
||||
runtime.rs // overview / segment detail / station detail
|
||||
```
|
||||
|
||||
### 5.2 段状态机
|
||||
|
||||
对应说明书 13.6:
|
||||
|
||||
| SegmentState | 含义 | 出口 |
|
||||
| --- | --- | --- |
|
||||
| `Idle` | 等待 auto 启动 | → `Checking` |
|
||||
| `Checking` | 评估 `start_allow` / `start_deny` 联锁 | 通过 → `Executing`;否则 → `Blocked` |
|
||||
| `Executing` | 已发出当前 step 的命令 | → `Confirming` |
|
||||
| `Confirming` | 等待 `confirm_signal` 到位 | 收到 → 下一步;超时 → `Faulted` |
|
||||
| `Resetting` | 等待执行机构复位(如顶车机后退) | → 下一步或 `Completed` |
|
||||
| `Completed` | 段完成,输出完成信号 | 回 `Idle`(自动循环段) |
|
||||
| `Blocked` | 允许条件不满足 | 条件再次满足 → `Checking` |
|
||||
| `Faulted` | 故障或超时 | 故障解除 + 满足复位 → `ManualAckRequired` 或 `Idle` |
|
||||
| `ManualAckRequired` | 等待人工确认 | API ack → `Idle` |
|
||||
|
||||
### 5.3 段内执行循环
|
||||
|
||||
伪代码:
|
||||
|
||||
```
|
||||
loop {
|
||||
reload segment + steps + interlocks
|
||||
run check_interlocks(state, run_halt) // 运行中停机检测
|
||||
match state {
|
||||
Idle if auto_enabled => state = Checking,
|
||||
Checking => {
|
||||
if pass(start_allow) && !any(start_deny) {
|
||||
step = first_step
|
||||
state = Executing
|
||||
} else {
|
||||
blocked_reason = ...
|
||||
state = Blocked
|
||||
}
|
||||
}
|
||||
Executing => {
|
||||
execute(step.action_kind, step.target_*) // 发命令
|
||||
state = Confirming
|
||||
}
|
||||
Confirming => {
|
||||
wait_signal(step.confirm_*, step.timeout_ms)
|
||||
on timeout → fault / retry / block by step.on_timeout
|
||||
on ok → next_step_no_on_success or next step or Completed
|
||||
}
|
||||
Faulted => break and wait manual recovery
|
||||
...
|
||||
}
|
||||
notify or fault_tick
|
||||
}
|
||||
```
|
||||
|
||||
`wait_signal` 复用与 feeder `wait_phase` 类似的 `tokio::select! { sleep_until(deadline), notify, fault_tick }` 模式,但终止条件是"绑定信号到达期望值"而非时间到。
|
||||
|
||||
### 5.4 step_executor
|
||||
|
||||
集中处理 `action_kind` 到具体写点动作:
|
||||
|
||||
- 短命令类 `action_kind` 调 `plc_platform_core::control::command::send_pulse_command`。
|
||||
- 持续命令类 `action_kind` 先写 `command_role`,确认到位、超时或故障中断时按 `stop_command_role` 收尾。
|
||||
- `transfer_move_to`:写目标工位编号到摆渡车定位命令点位,等待 `arrived` 信号。
|
||||
- `wait_signal`:不发命令。
|
||||
- 各设备的 `start_cmd / stop_cmd / open_cmd / close_cmd` 信号角色复用 feeder 已有的 `signal_role` 命名空间,equipment 表无需新表结构。
|
||||
|
||||
命令执行前必须重新检查:
|
||||
|
||||
- 设备 `rem=true`
|
||||
- 设备 `flt=false`
|
||||
- 命令点与确认点 `quality=Good`
|
||||
- 当前段仍处于允许执行模式
|
||||
- 当前 step 仍是 runtime 中的 `current_step_no`
|
||||
|
||||
## 6. 联锁与异常
|
||||
|
||||
### 6.1 联锁判定顺序(对齐说明书 8.1 / 13)
|
||||
|
||||
1. 通信质量(任一绑定点 quality != Good) → `comm_locked`
|
||||
2. 就地 / 远程状态(`rem=false`)→ 停止自动并转人工恢复
|
||||
3. 安全联锁 / 急停 → `Faulted`
|
||||
4. 设备故障(`flt` = true) → `Faulted`
|
||||
5. 门位联锁
|
||||
6. 机械臂联锁
|
||||
7. 工艺允许条件(空位 / 到位)
|
||||
8. 普通顺控条件
|
||||
|
||||
高优先级不满足时低优先级不再判断。
|
||||
|
||||
### 6.2 通用允许检查(自动注入到每段)
|
||||
|
||||
每段无论是否有显式 `segment_interlock`,引擎都执行以下通用检查(说明书 13.1):
|
||||
|
||||
- 目标工位空位
|
||||
- 本工位有车或动作前提
|
||||
- 执行机构原位
|
||||
- 设备无故障
|
||||
- 设备处于远程
|
||||
- 信号质量正常
|
||||
- 段引用的资源未被占用
|
||||
|
||||
### 6.3 异常恢复(说明书 13.5)
|
||||
|
||||
- 故障优先停止当前 step 的命令。
|
||||
- `Faulted` 保留 `current_step_no`,不跳步。
|
||||
- `remote_manual` 下允许人工执行复位动作,但复位动作仍执行安全、故障、门位和通信检查。
|
||||
- 故障物理消失后:
|
||||
- 若 `require_manual_ack_after_fault`(默认 true) → `ManualAckRequired`
|
||||
- 否则自动回 `Idle`。
|
||||
- `POST /api/control/segment/{id}/ack-fault` 用于人工确认。
|
||||
|
||||
## 7. 公共资源调度
|
||||
|
||||
说明书 3.3 / 3.4 指出:前端码车系统、窑尾摆渡、回车线、卸砖线为公共段,1 号 / 2 号窑线在此处汇合。
|
||||
|
||||
实现:
|
||||
|
||||
```rust
|
||||
pub struct ResourceRegistry {
|
||||
inner: RwLock<HashMap<String, ResourceLease>>,
|
||||
}
|
||||
|
||||
pub struct ResourceLease {
|
||||
pub owner_segment_id: Uuid,
|
||||
pub acquired_at: DateTime<Utc>,
|
||||
pub heartbeat_at: DateTime<Utc>,
|
||||
}
|
||||
```
|
||||
|
||||
资源 key 示例:`transfer_front` / `transfer_tail` / `robot_arm` / `unload_position` / `return_line`。
|
||||
|
||||
段配置中以新表 `segment_resource(segment_id, resource_key)` 声明所需资源;段进入 `Executing` 前必须 `try_acquire`,进入 `Completed` 时 `release`。冲突时停留 `Blocked`,附 `blocked_reason = "resource_busy: transfer_front"`。
|
||||
|
||||
资源恢复策略:
|
||||
|
||||
- 资源持有段每个状态循环刷新 `heartbeat_at`。
|
||||
- 若 owner task 已退出、段被禁用、或 owner 已回到 `Idle/Completed`,supervisor 可回收租约。
|
||||
- `Faulted` 时是否释放资源按资源类型决定:机械臂区、卸砖位等可释放;摆渡车正在载车时不释放,必须人工确认或到达安全位后释放。
|
||||
- 资源等待超时只报警和进入 `Blocked`,不抢占低优先级段。首期不做死锁自动解除。
|
||||
|
||||
## 8. 事件与 WebSocket
|
||||
|
||||
### 8.1 业务事件命名空间 `ops.*`
|
||||
|
||||
| event_type | level |
|
||||
| --- | --- |
|
||||
| `ops.segment.auto_started` | info |
|
||||
| `ops.segment.auto_stopped` | info |
|
||||
| `ops.segment.step_advanced` | info |
|
||||
| `ops.segment.completed` | info |
|
||||
| `ops.segment.blocked` | warn |
|
||||
| `ops.segment.fault_locked` | error |
|
||||
| `ops.segment.fault_acked` | info |
|
||||
| `ops.segment.comm_locked` | warn |
|
||||
| `ops.segment.comm_recovered` | info |
|
||||
| `ops.station.state_changed` | info |
|
||||
| `ops.alarm.action_timeout` | error |
|
||||
| `ops.alarm.signal_conflict` | error |
|
||||
| `ops.alarm.resource_busy` | warn |
|
||||
|
||||
所有事件经 `record_event` 落 `event` 表(复用平台机制)。
|
||||
|
||||
### 8.2 WebSocket 消息扩展
|
||||
|
||||
不把 ops 的 `SegmentRuntime` 类型放进 core。`plc_platform_core::websocket::WsMessage` 增加一个通用业务消息分支,业务 payload 由 app crate 构造:
|
||||
|
||||
```rust
|
||||
pub enum WsMessage {
|
||||
// 已有 ...
|
||||
AppEvent(AppWsEvent),
|
||||
}
|
||||
|
||||
pub struct AppWsEvent {
|
||||
pub app: String, // "operation-system"
|
||||
pub event_type: String, // "segment_runtime_changed" / "station_state_changed"
|
||||
pub data: serde_json::Value,
|
||||
}
|
||||
```
|
||||
|
||||
ops 侧约定:
|
||||
|
||||
- `event_type="segment_runtime_changed"`:`data` 序列化 `SegmentRuntime`。
|
||||
- `event_type="station_state_changed"`:`data` 包含 `station_id / presence / vacancy / arrived / updated_at`。
|
||||
- feeder 前端忽略未知 `AppEvent` 或非本 app 的消息;ops 前端只处理 `app="operation-system"`。
|
||||
|
||||
> 这样仍保留单一 websocket 入口,但 core 不需要知道 ops 的领域模型。
|
||||
|
||||
## 9. API 设计
|
||||
|
||||
### 9.1 配置 API
|
||||
|
||||
```
|
||||
GET /api/station
|
||||
POST /api/station
|
||||
GET /api/station/{id}
|
||||
PUT /api/station/{id}
|
||||
DELETE /api/station/{id}
|
||||
POST /api/station/{id}/signal // 绑定信号
|
||||
DELETE /api/station/{id}/signal/{role}
|
||||
|
||||
GET /api/segment
|
||||
POST /api/segment
|
||||
GET /api/segment/{id}
|
||||
GET /api/segment/{id}/detail // 含 step / interlock / resource
|
||||
PUT /api/segment/{id}
|
||||
DELETE /api/segment/{id}
|
||||
POST /api/segment/{id}/step
|
||||
PUT /api/segment/{id}/step/{step_no}
|
||||
DELETE /api/segment/{id}/step/{step_no}
|
||||
POST /api/segment/{id}/interlock
|
||||
DELETE /api/segment/{id}/interlock/{interlock_id}
|
||||
```
|
||||
|
||||
### 9.2 控制 API
|
||||
|
||||
```
|
||||
POST /api/control/segment/{id}/start-auto
|
||||
POST /api/control/segment/{id}/stop-auto
|
||||
POST /api/control/segment/{id}/reset // 强制回 Idle,仅在 Faulted/Blocked 状态可用
|
||||
POST /api/control/segment/{id}/ack-fault
|
||||
POST /api/control/segment/{id}/manual-step // remote_manual 下单步执行
|
||||
POST /api/control/segment/batch-start-auto
|
||||
POST /api/control/segment/batch-stop-auto
|
||||
|
||||
POST /api/control/equipment/{id}/manual-action // remote_manual 下单设备动作,仍执行联锁
|
||||
```
|
||||
|
||||
### 9.3 运行态 API
|
||||
|
||||
```
|
||||
GET /api/runtime/overview // 所有段 + 关键工位 + 报警计数
|
||||
GET /api/runtime/segment/{id}
|
||||
GET /api/runtime/station/{id}
|
||||
GET /api/event?type=ops.*
|
||||
```
|
||||
|
||||
## 10. 前端
|
||||
|
||||
复用 `web/core` 的源码、点位、设备、事件、日志、文档抽屉。
|
||||
|
||||
`web/ops/` 增加:
|
||||
|
||||
- 总览页:双窑线 + 公共段流程图(首版静态 SVG + 区域绑定段 / 工位状态)
|
||||
- 段卡片列表:展示 `state / current_step / blocked_reason / fault_message`
|
||||
- 工位状态视图:有车 / 空位 / 到位
|
||||
- 配置页:站点 / 段 / step / interlock 表格 + 表单
|
||||
- 手动操作:段启停 / 故障确认 / 复位
|
||||
|
||||
WebSocket 订阅 `AppEvent(app="operation-system")`,按 `event_type` 分发 `segment_runtime_changed` 和 `station_state_changed` 实时刷新。
|
||||
|
||||
## 11. 复用 vs 新增对照
|
||||
|
||||
| 模块 | 来源 | 用途 |
|
||||
| --- | --- | --- |
|
||||
| `plc_platform_core::connection` | 复用 | OPC UA 读写 |
|
||||
| `plc_platform_core::control::command::send_pulse_command` | 复用 | 所有动作命令底层 |
|
||||
| `plc_platform_core::event::record_event` + `EventInsert` | 复用 | 事件落库 |
|
||||
| `plc_platform_core::event::MetadataCache` | 复用 + 扩展 | 通用化为按 `(table, id)` 查 code;feeder 用 unit/equipment,ops 加 station/segment |
|
||||
| `plc_platform_core::websocket::WsMessage` | 重构 | 删除 `UnitRuntimeChanged`(feeder 业务),新增通用 `AppEvent(AppWsEvent)`;feeder 和 ops 都走 AppEvent |
|
||||
| `plc_platform_core::handler::platform_routes` | 复用 | source / point / equipment / tag / page |
|
||||
| `plc_platform_core::model::ControlUnit` | **迁出 core** | P-1 阶段下放到 feeder;语义本就是 feeder 业务 |
|
||||
| `plc_platform_core::control::runtime::{UnitRuntime, ControlRuntimeStore}` | **迁出 core** | 同上,含 `DistributorRunning` 这种 feeder 专属状态 |
|
||||
| `plc_platform_core::service::control` unit CRUD | **迁出 core** | 下放到 feeder;event 查询留 core |
|
||||
| `app_feeder_distributor::control::*` | **不复用** | 结构参考 |
|
||||
|
||||
> **P-1 阶段说明**:上表中的"迁出 core"是清理动作,发生在 P0 之前。详见 §12。
|
||||
|
||||
## 12. 阶段计划
|
||||
|
||||
| 阶段 | 目标 | 主要工作 |
|
||||
| --- | --- | --- |
|
||||
| **P-1 Core 业务清理** | core 不再持有 feeder 业务模型 | 把 `UnitRuntime / UnitRuntimeState / ControlRuntimeStore / ControlUnit / unit CRUD / WsMessage::UnitRuntimeChanged` 从 `plc_platform_core` 迁到 `app_feeder_distributor`;`WsMessage` 新增 `AppEvent(AppWsEvent)` 分支并删除 `UnitRuntimeChanged`;feeder 引用全部调整;前端 ws 客户端按 `app + event_type` 分发;`MetadataCache` 通用化为 `entity_code(table, id)`。零行为变更,feeder 通过现有 smoke test |
|
||||
| **P0 骨架对齐** | `app_operation_system` 与 feeder 在依赖、AppState、bootstrap、tray、启动/退出链路对齐 | Cargo.toml 补依赖;AppState 加 `EventManager` + `SegmentRuntimeStore` + `ResourceRegistry`;启动接 `connect_all_enabled_sources`;启动 engine supervisor;退出时断开数据源 |
|
||||
| **P1 数据库迁移 & 模型** | ops 配置表 + event 归因字段 + Rust model | 新 migration `2026-05-1x_create_operation_system.sql`;新增 `station / station_signal / process_segment / segment_step / segment_interlock / segment_resource`;扩展 `event.subject_type/subject_id`;`app_operation_system::model` 模块 |
|
||||
| **P2 配置 API** | 站点 / 段 / step / interlock CRUD | `service::station / segment`;handler;router |
|
||||
| **P3 引擎 MVP** | 跑通 1 个段端到端(前端码车位进车段,说明书 8.2) | `engine`、`step_executor`、`interlock`、`runtime`;通用 `AppEvent` WebSocket 推送 |
|
||||
| **P4 动作模板补全** | 覆盖 8 章 + 10 章典型动作 | 各 `action_kind` 实现 + simulate 反馈 |
|
||||
| **P5 双窑线段模板化** | 通过段配置实现 1 号 / 2 号窑线 4 段(进口 / 内前移 / 出口) | 段配置 seed;端到端跑通 |
|
||||
| **P6 资源调度** | 公共段互斥 | `ResourceRegistry`;`segment_resource` 表;Blocked 路径完善 |
|
||||
| **P7 公共段** | 摆渡车 / 卸砖 / 回车线 | 段实例 + 段间交接 |
|
||||
| **P8 报警 & 异常恢复** | 超时报警、信号冲突、人工确认完整链路 | `AppEvent::Alarm*`;ack-fault API |
|
||||
| **P9 前端监控页** | 段卡片 + 工位状态 + 流程图 | `web/ops/html` + JS |
|
||||
| **P10 配置前端** | 段 / 工位 / 联锁可视化配置 | `web/ops/html` 表格表单 |
|
||||
|
||||
每阶段都要求:
|
||||
|
||||
- `cargo build -p app_operation_system` 通过
|
||||
- 至少 1 个单元测试或 smoke test
|
||||
- 不破坏 `app_feeder_distributor` 编译
|
||||
|
||||
## 13. 风险与约束
|
||||
|
||||
### 13.1 主要风险
|
||||
|
||||
- **P-1 迁移破坏 feeder**:从 core 把 unit 模型迁到 feeder 时容易漏改 import 或 ws 客户端调用。要求迁移单独成 commit,feeder 启动 + 单元测试 + ws 推送链路逐项验证。
|
||||
- **现场 I/O 清单缺失**:说明书描述了逻辑关系但未明确每个工位 / 设备对应的具体点位。落地前必须补 I/O 对照表。
|
||||
- **段切分粒度**:段切得太细 → 状态机膨胀;切得太粗 → 段内步骤过多。首期建议按说明书章节级切(一节 = 一段)。
|
||||
- **WebSocket 领域边界**:不得把 `SegmentRuntime` 放入 core,否则 core 会反向依赖 ops 业务模型;采用通用 `AppEvent` payload。
|
||||
- **公共资源死锁**:例如摆渡车被段 A 占用、段 A 又等卸砖位空(被段 B 占用)。首期通过段优先级与超时报警缓解,不引入死锁检测。
|
||||
- **持续命令收尾**:输送、顶车、拉引等不是纯脉冲动作,必须在超时、故障和模式切换时明确停止命令。
|
||||
|
||||
### 13.2 约束
|
||||
|
||||
- 首期不做规则引擎,所有联锁靠固定 `rule_kind` 枚举。
|
||||
- 首期段 / step 改动不做热加载——supervisor 每 10s 重读配置,与 feeder 一致。
|
||||
- 首期 `segment_runtime` 不持久化,重启全部回 `Idle`。
|
||||
- 首期不做资源抢占;资源冲突只阻塞、报警和等待人工处理。
|
||||
|
||||
## 14. 验收标准
|
||||
|
||||
完成 P0–P5 后应达到:
|
||||
|
||||
- 仓库新增 6 张 ops 业务配置表,并扩展 `event.subject_type/subject_id`,与 feeder 业务表互不干扰。
|
||||
- `app_operation_system` 可独立编译为 exe,可启动并连接 OPC UA 数据源。
|
||||
- 启动后具备 `EventManager`、`SegmentRuntimeStore`、`ResourceRegistry`、engine supervisor,退出时可断开数据源。
|
||||
- 至少 1 条段(例如 2 号干燥窑进口段,含 8 步)可通过配置驱动跑通:
|
||||
- 自动启停
|
||||
- 步骤顺序推进
|
||||
- 闭环信号确认
|
||||
- 持续动作到位后停止命令
|
||||
- 故障停步 + 人工确认
|
||||
- WebSocket 通过 `AppEvent(app="operation-system")` 推送段运行态变化、工位状态变化。
|
||||
- 前端可见段卡片与当前步骤进度。
|
||||
- `event` 表能按 `ops.*` 和 `subject_type/subject_id` 查到全链路事件。
|
||||
|
||||
完成 P6–P10 后应达到:
|
||||
|
||||
- 1 号 / 2 号窑线全部 6 段(进口 / 内前移 / 出口 × 2 窑)跑通。
|
||||
- 公共段(前端码车、摆渡车、窑尾、卸砖、回车)跑通。
|
||||
- 报警分类齐全(说明书 13.4 全部 10 类)。
|
||||
- 监控前端 + 配置前端可用。
|
||||
|
||||
## 15. 后续可演进项(非首期)
|
||||
|
||||
- 联锁 `expression` 类型:引入简单布尔表达式语言,替代 `rule_kind` 枚举。
|
||||
- 段历史持久化:将每段每次完成 / 故障写入 `segment_run_history`,支持时间线回放。
|
||||
- 现场调试视图:模拟点位值、单步推进、跳步授权(带操作员签名)。
|
||||
- 公共能力下沉:若后续出现第三套类似业务,再把 segment 引擎抽到 `plc_platform_core::control::segment`。
|
||||
Binary file not shown.
Loading…
Reference in New Issue