581 lines
11 KiB
Markdown
581 lines
11 KiB
Markdown
# 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),自动控制需操作员手动重新启动。
|