plc_control/docs/api-feeder.md

581 lines
11 KiB
Markdown
Raw 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.

# PLC Control API
本文档基于当前后端代码整理,覆盖 HTTP API、SSE 日志流和 WebSocket 实时消息。
## 基本信息
- UI: `/ui`
- HTTP API 前缀: `/api`
- 公共 WebSocket: `/ws/public`
- 客户端专属 WebSocket: `/ws/client/{client_id}`
- 日志 SSE: `/api/logs/stream`
## 通用错误响应
失败时通常返回:
```json
{
"err_code": 400,
"err_msg": "Invalid JSON format",
"err_detail": null
}
```
常见状态码:
- `400 Bad Request`: 参数非法或业务前置条件不满足
- `403 Forbidden`: 控制条件不满足或无写入权限
- `404 Not Found`: 资源不存在
- `500 Internal Server Error`: 服务端内部错误
## Source
### GET `/api/source`
获取数据源列表及连接状态。
### POST `/api/source`
创建数据源。
请求示例:
```json
{
"name": "PLC-1",
"protocol": "opcua",
"endpoint": "opc.tcp://127.0.0.1:4840",
"enabled": true
}
```
### PUT `/api/source/{source_id}`
更新数据源,字段均可选。
### DELETE `/api/source/{source_id}`
删除数据源。成功返回 `204 No Content`
### POST `/api/source/{source_id}/reconnect`
手动重连数据源。
### POST `/api/source/{source_id}/browse`
从 OPC UA 数据源浏览节点并写入本地 `node` 表。
### GET `/api/source/{source_id}/node-tree`
获取数据源节点树。
## Point
### GET `/api/point`
分页获取点位列表,同时返回实时监测值。
查询参数:
- `source_id`
- `equipment_id`
- `page`
- `page_size`
### GET `/api/point/{point_id}`
获取单个点位。
### GET `/api/point/{point_id}/history`
获取最近历史样本。历史数据保存在进程内环形缓冲区,服务重启后清空。
查询参数:
- `limit`: 默认 `120`,最大 `1000`
### PUT `/api/point/{point_id}`
更新点位元数据。
可更新字段:
```json
{
"name": "Temperature",
"description": "Room temperature",
"unit": "C",
"tag_id": "uuid",
"equipment_id": "uuid",
"signal_role": "run"
}
```
说明:
- 点位变更设备绑定或信号角色后,会唤醒相关控制单元任务,使控制引擎尽快使用最新映射。
### DELETE `/api/point/{point_id}`
删除单个点位。
说明:
- 删除后会同步通知相关控制单元刷新配置。
### POST `/api/point/batch`
根据节点批量创建点位。
### DELETE `/api/point/batch`
批量删除点位。
说明:
- 删除后会同步通知相关控制单元刷新配置。
### PUT `/api/point/batch/set-equipment`
批量设置点位设备绑定和信号角色。
```json
{
"point_ids": ["uuid1", "uuid2"],
"equipment_id": "uuid",
"signal_role": "run"
}
```
说明:
- 更新前后关联到的控制单元都会被唤醒,避免控制引擎继续使用旧映射。
### PUT `/api/point/batch/set-tags`
批量设置点位标签。
### POST `/api/point/value/batch`
批量写点。
请求头:
- `X-Write-Key: <key>`
请求示例:
```json
{
"items": [
{ "point_id": "uuid", "value": 12.3 }
]
}
```
## Equipment
### GET `/api/equipment`
分页获取设备列表,包含点位数量和已绑定信号角色点。
### GET `/api/equipment/{equipment_id}`
获取单个设备。
### GET `/api/equipment/{equipment_id}/points`
获取设备下所有点位。
### POST `/api/equipment`
创建设备。
说明:
- 如果设备绑定了控制单元,创建成功后会唤醒对应控制单元。
### PUT `/api/equipment/{equipment_id}`
更新设备。
说明:
- 如果设备更换了 `unit_id`、`kind` 或其他控制相关配置,旧单元和新单元都会被唤醒。
### PUT `/api/equipment/batch/set-unit`
批量调整设备所属控制单元。
说明:
- 批量更新前关联到的旧单元,以及更新后关联到的新单元,都会收到唤醒通知。
### DELETE `/api/equipment/{equipment_id}`
删除设备。成功返回 `204 No Content`
说明:
- 删除后会唤醒原所属控制单元。
## Unit
### GET `/api/unit`
分页获取控制单元列表,返回单元基础信息、运行时快照和设备摘要。
### POST `/api/unit`
创建控制单元。
请求示例:
```json
{
"code": "U01",
"name": "1号机组",
"description": null,
"enabled": true,
"run_time_sec": 60,
"stop_time_sec": 30,
"acc_time_sec": 3600,
"bl_time_sec": 10,
"require_manual_ack_after_fault": true
}
```
约束:
- `run_time_sec > 0`
- `stop_time_sec > 0`
- `acc_time_sec > 0`
- `bl_time_sec > 0`
- `acc_time_sec > run_time_sec`
### PUT `/api/unit/{unit_id}`
更新控制单元,字段均可选。
约束:
- 如果更新后涉及时长字段,仍然必须满足上面的全部约束。
### DELETE `/api/unit/{unit_id}`
删除控制单元。成功返回 `204 No Content`
### GET `/api/unit/{unit_id}`
获取单个控制单元及其设备摘要。
### GET `/api/unit/{unit_id}/detail`
获取控制单元完整详情,包括设备和点位列表。
### GET `/api/unit/{unit_id}/runtime`
获取控制单元当前运行时状态。
响应字段:
```json
{
"unit_id": "uuid",
"state": "running",
"auto_enabled": true,
"accumulated_run_sec": 3600000,
"display_acc_sec": 3600000,
"fault_locked": false,
"flt_active": false,
"comm_locked": false,
"manual_ack_required": false,
"rem_local": false
}
```
`rem_local` 说明:
- `true`: 单元下存在设备 REM 信号为本地模式REM=0信号质量正常
- `rem_local = true` 时,自动控制会被强制停止,且禁止重新启动,直到所有 REM 恢复远程
`state` 枚举:
- `stopped`
- `running`
- `distributor_running`
- `fault_locked`
- `comm_locked`
说明:
- `accumulated_run_sec``display_acc_sec` 单位都是毫秒。
- 运行时状态保存在内存中,服务重启后重置。
## Event
### GET `/api/event`
分页获取系统事件。
查询参数:
- `unit_id`
- `event_type`
- `page`
- `page_size`
常见事件类型:
- `unit.auto_control_started`
- `unit.auto_control_stopped`
- `unit.fault_locked`
- `unit.fault_acked`
- `unit.comm_locked`
- `unit.comm_recovered`
- `unit.rem_local` — 设备切换为本地模式自动控制已停止warn
- `unit.rem_recovered` — 设备切换回远程模式自动控制需手动重新启动warn
- `equipment.start_command_sent`
- `equipment.stop_command_sent`
## Control
所有控制命令在执行前都会校验:
- 信号质量
- REM 状态REM=0 时拒绝手动指令)
- FLT 状态
- 单元 `auto_enabled`
- 单元 `comm_locked`
- 单元 `fault_locked`
- 单元 `manual_ack_required`
- 单元 `rem_local`(自动控制启动时额外校验)
### POST `/api/control/equipment/{equipment_id}/start`
发送设备启动脉冲命令。
### POST `/api/control/equipment/{equipment_id}/stop`
发送设备停止脉冲命令。
### POST `/api/control/unit/{unit_id}/start-auto`
启动单元自动控制。
前置条件:
- 单元已启用
- `fault_locked = false`
- `comm_locked = false`
- `manual_ack_required = false`
- `rem_local = false`(有设备处于本地模式时不允许启动)
成功响应:
```json
{ "ok_msg": "Auto control started", "unit_id": "uuid" }
```
### POST `/api/control/unit/{unit_id}/stop-auto`
停止单元自动控制。
### POST `/api/control/unit/batch-start-auto`
批量启动所有已启用单元的自动控制。
会跳过以下单元:
- 已经 `auto_enabled = true`
- `fault_locked = true`
- `comm_locked = true`
- `manual_ack_required = true`
- `rem_local = true`
说明:
- 单个启动和批量启动现在使用相同的阻断规则。
### POST `/api/control/unit/batch-stop-auto`
批量停止自动控制。
### POST `/api/control/unit/{unit_id}/ack-fault`
人工确认故障。
前置条件:
- `fault_locked = true`
- `flt_active = false`
## Tag
### GET `/api/tag`
分页获取标签列表。
### POST `/api/tag`
创建标签。
### GET `/api/tag/{tag_id}`
获取标签下已绑定点位。
### PUT `/api/tag/{tag_id}`
更新标签。
### DELETE `/api/tag/{tag_id}`
删除标签。成功返回 `204 No Content`
## Page
### GET `/api/page`
获取自定义页面列表。
### POST `/api/page`
创建页面。
### GET `/api/page/{page_id}`
获取单个页面。
### PUT `/api/page/{page_id}`
更新页面。
### DELETE `/api/page/{page_id}`
删除页面。成功返回 `204 No Content`
## Log
### GET `/api/logs`
读取日志文件内容。默认读取最新的 `app.log*` 文件。
查询参数:
- `file`: 指定文件名,仅允许 `app.log*`
- `cursor`: 增量读取位置
- `tail_lines`: 默认 `200`,最大 `2000`
- `max_bytes`: 默认 `64KB`,最大 `512KB`
响应示例:
```json
{
"file": "app.log",
"cursor": 204800,
"lines": ["2026-03-25 10:00:00 INFO ..."],
"truncated": false,
"reset": false
}
```
字段说明:
- `truncated = true`: 当前还有未读完内容,可继续用新 `cursor` 拉取
- `reset = true`: 文件被截断或读取起点被重置
### GET `/api/logs/stream`
通过 SSE 推送日志增量。
查询参数:
- `file`
- `cursor`
- `max_bytes`
默认行为:
- 如果没有传 `file`,服务端会始终跟随最新的 `app.log*`
- 如果日志轮转到了新文件,流会自动切换到新文件,并推送一条 `reset = true` 的日志事件
事件示例:
```text
event: log
data: { "file": "app.log.1", "cursor": 1024, "lines": ["..."], "truncated": false, "reset": true }
event: error
data: log stream read failed
```
## WebSocket
### 服务端推送消息
#### `PointNewValue`
点位实时值更新。
#### `EventCreated`
系统事件创建。
#### `UnitRuntimeChanged`
控制单元运行时状态更新。
```json
{
"type": "UnitRuntimeChanged",
"data": {
"unit_id": "uuid",
"state": "running",
"auto_enabled": true,
"fault_locked": false,
"flt_active": false,
"comm_locked": false,
"manual_ack_required": false,
"rem_local": false,
"accumulated_run_sec": 3600000,
"display_acc_sec": 3600000
}
}
```
#### `PointSetValueBatchResult`
批量写点结果回调。
### 客户端消息
#### `auth_write`
```json
{
"type": "auth_write",
"data": { "key": "your-write-key" }
}
```
#### `point_set_value_batch`
```json
{
"type": "point_set_value_batch",
"data": {
"items": [
{ "point_id": "uuid", "value": 12.3 }
]
}
}
```
## 备注
- 控制引擎现在会在每轮单元循环中重新加载设备和角色映射,而不是只在任务启动时加载一次。
- 设备和点位的控制相关配置变更后,会主动唤醒对应单元任务,使新配置尽快生效。
- 日志流默认跟随最新日志文件,适配日志轮转场景。
- REM 信号为本地模式REM=0手动控制指令start/stop和自动控制启动均被拒绝若自动控制正在运行引擎会立即停止并记录 `unit.rem_local` 事件。REM 恢复远程后记录 `unit.rem_recovered`warn自动控制需操作员手动重新启动。