plc_control/API.md

656 lines
12 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 接口说明
本文档基于当前服务端路由与处理器代码整理,覆盖 HTTP API 和 WebSocket 实时消息。
## 基本信息
- 服务端默认提供静态 UI`/ui`
- HTTP API 前缀:`/api`
- 公共实时 WebSocket`/ws/public`
- 客户端专属 WebSocket`/ws/client/{client_id}`
## 通用错误响应
接口失败时通常返回:
```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`
获取所有已启用数据源及其连接状态。
响应示例:
```json
[
{
"id": "uuid",
"name": "PLC-1",
"protocol": "opcua",
"endpoint": "opc.tcp://127.0.0.1:4840",
"security_policy": null,
"security_mode": null,
"enabled": true,
"created_at": "2026-03-20 10:00:00.000",
"updated_at": "2026-03-20 10:00:00.000",
"is_connected": true,
"last_error": null,
"last_time": "2026-03-20 10:05:00.000"
}
]
```
### POST `/api/source`
创建数据源。
请求体:
```json
{
"name": "PLC-1",
"endpoint": "opc.tcp://127.0.0.1:4840",
"enabled": true
}
```
响应:
```json
{ "id": "uuid" }
```
### PUT `/api/source/{source_id}`
更新数据源。请求体字段均可选:
```json
{
"name": "PLC-1",
"endpoint": "opc.tcp://127.0.0.1:4840",
"enabled": true,
"security_policy": "None",
"security_mode": "None",
"username": "user",
"password": "pass"
}
```
### DELETE `/api/source/{source_id}`
删除数据源。成功响应:`204 No Content`
### POST `/api/source/{source_id}/reconnect`
手动重连指定数据源。
```json
{ "ok_msg": "Source reconnected successfully" }
```
### POST `/api/source/{source_id}/browse`
从 OPC UA 源浏览节点并写入本地 `node` 表。
```json
{ "ok_msg": "Browse completed", "total_nodes": 123 }
```
### GET `/api/source/{source_id}/node-tree`
获取指定数据源的节点树(含 `children` 递归嵌套)。
---
## Point
### GET `/api/point`
分页获取点位列表,同时返回实时监测值。
查询参数:
- `source_id`:可选,按数据源过滤
- `equipment_id`:可选,按设备过滤
- `page`:页码
- `page_size`:每页条数(`-1` 表示全量)
响应示例:
```json
{
"data": [
{
"point": {
"id": "uuid",
"node_id": "uuid",
"name": "Temperature",
"equipment_id": "uuid",
"signal_role": "run",
"created_at": "2026-03-20 10:00:00.000",
"updated_at": "2026-03-20 10:00:00.000"
},
"point_monitor": {
"point_id": "uuid",
"timestamp": "2026-03-20 10:05:00.000",
"quality": "good",
"value": 12.3,
"value_type": "float",
"value_text": "12.3"
}
}
],
"total": 1,
"page": 1,
"page_size": 100
}
```
### GET `/api/point/{point_id}`
获取单个点位。
### GET `/api/point/{point_id}/history`
获取点位最近历史样本(进程内存环形缓冲,重启后清空)。
查询参数:`limit`(可选,默认 `120`,最大 `1000`
```json
[
{
"timestamp": "2026-03-20 10:05:00.000",
"quality": "good",
"value": 12.3,
"value_text": "12.3",
"value_number": 12.3
}
]
```
### PUT `/api/point/{point_id}`
更新点位元数据,字段均可选:
```json
{
"name": "Temperature",
"description": "Room temperature",
"unit": "°C",
"equipment_id": "uuid",
"signal_role": "run"
}
```
### DELETE `/api/point/{point_id}`
删除单个点位。
### POST `/api/point/batch`
根据节点批量创建点位。
```json
{ "node_ids": ["uuid1", "uuid2"] }
```
响应:
```json
{
"success_count": 2,
"failed_count": 0,
"failed_node_ids": [],
"created_point_ids": ["uuid3", "uuid4"]
}
```
### DELETE `/api/point/batch`
批量删除点位。
```json
{ "point_ids": ["uuid1", "uuid2"] }
```
### PUT `/api/point/batch/set-equipment`
批量设置点位的设备绑定和信号角色。
```json
{
"point_ids": ["uuid1", "uuid2"],
"equipment_id": "uuid",
"signal_role": "run"
}
```
### POST `/api/point/value/batch`
批量写点。
请求头:`X-Write-Key: <key>`
```json
{
"items": [
{ "point_id": "uuid", "value": 12.3 }
]
}
```
---
## Equipment
### GET `/api/equipment`
分页获取设备列表,包含每台设备绑定的点位数量。
查询参数:`page`、`page_size``-1` 全量)、`keyword`(可选,按 code/name 模糊搜索)
响应示例:
```json
{
"data": [
{
"id": "uuid",
"unit_id": "uuid",
"code": "E01",
"name": "投煤器1",
"kind": "coal_feeder",
"description": null,
"created_at": "2026-03-20 10:00:00.000",
"updated_at": "2026-03-20 10:00:00.000",
"point_count": 5
}
],
"total": 1,
"page": 1,
"page_size": 20
}
```
### POST `/api/equipment`
创建设备。
```json
{
"unit_id": "uuid",
"code": "E01",
"name": "投煤器1",
"kind": "coal_feeder",
"description": null
}
```
响应:`201 Created`
```json
{ "id": "uuid", "ok_msg": "Equipment created successfully" }
```
### PUT `/api/equipment/{equipment_id}`
更新设备,字段均可选:
```json
{
"unit_id": "uuid",
"code": "E01",
"name": "投煤器1",
"kind": "coal_feeder",
"description": null
}
```
### DELETE `/api/equipment/{equipment_id}`
删除设备。成功响应:`204 No Content`
### GET `/api/equipment/{equipment_id}/points`
获取指定设备下所有绑定点位。
### PUT `/api/equipment/batch/set-unit`
批量将设备绑定到控制单元。
```json
{
"equipment_ids": ["uuid1", "uuid2"],
"unit_id": "uuid"
}
```
---
## Unit控制单元
### GET `/api/unit`
分页获取控制单元列表。
查询参数:`page`、`page_size`、`keyword`(可选)
响应字段包含:`id`、`code`、`name`、`enabled`、`run_time_sec`、`stop_time_sec`、`acc_time_sec`、`bl_time_sec`、`require_manual_ack_after_fault`
### 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
}
```
响应:`201 Created`
```json
{ "id": "uuid", "ok_msg": "Unit created successfully" }
```
### GET `/api/unit/{unit_id}`
获取单个控制单元。
### PUT `/api/unit/{unit_id}`
更新控制单元,字段均可选。
### DELETE `/api/unit/{unit_id}`
删除控制单元。成功响应:`204 No Content`
### GET `/api/unit/{unit_id}/runtime`
获取控制单元的当前运行时状态(内存中,不持久化)。
响应示例:
```json
{
"unit_id": "uuid",
"state": "running",
"auto_enabled": true,
"accumulated_run_sec": 3600000,
"current_run_elapsed_sec": 60000,
"current_stop_elapsed_sec": 0,
"distributor_run_elapsed_sec": 0,
"fault_locked": false,
"flt_active": false,
"comm_locked": false,
"manual_ack_required": false,
"last_tick_at": "2026-03-25 10:00:00.000"
}
```
`state` 枚举值:`stopped` / `running` / `distributor_running` / `fault_locked` / `comm_locked`
注意时间字段单位为毫秒ms
### GET `/api/unit/{unit_id}/detail`
获取控制单元及其下所有设备和点位的完整嵌套结构。
响应示例:
```json
{
"id": "uuid",
"code": "U01",
"name": "1号机组",
"enabled": true,
"equipments": [
{
"id": "uuid",
"code": "E01",
"name": "投煤器1",
"kind": "coal_feeder",
"points": [
{
"id": "uuid",
"name": "启动命令",
"signal_role": "start_cmd",
"equipment_id": "uuid"
}
]
}
]
}
```
---
## Event系统事件
### GET `/api/event`
分页获取系统控制事件记录。
查询参数:
- `unit_id`:可选,按控制单元过滤
- `event_type`:可选,按事件类型过滤
- `page`、`page_size`
响应示例:
```json
{
"data": [
{
"id": "uuid",
"event_type": "equipment.start_command_sent",
"level": "info",
"unit_id": "uuid",
"equipment_id": "uuid",
"message": "Start command sent to equipment ...",
"payload": {},
"created_at": "2026-03-25 10:00:00.000"
}
],
"total": 1,
"page": 1,
"page_size": 20
}
```
---
## Control控制命令
所有控制命令在执行前会校验信号质量、REM 状态、FLT 状态、单元通讯锁、单元故障锁。
### POST `/api/control/equipment/{equipment_id}/start`
向设备发送启动脉冲命令(写 HIGH → 延迟 300ms → 写 LOW
响应示例:
```json
{
"ok_msg": "Equipment start command sent",
"equipment_id": "uuid",
"unit_id": "uuid",
"command_role": "start_cmd",
"command_point_id": "uuid",
"pulse_ms": 300
}
```
失败(设备未处于可启动状态)返回 `403 Forbidden`
### POST `/api/control/equipment/{equipment_id}/stop`
向设备发送停止脉冲命令。响应结构同上。
### POST `/api/control/unit/{unit_id}/start-auto`
启动指定控制单元的自动控制循环。单元须已启用(`enabled = true`)。
```json
{ "ok_msg": "Auto control started", "unit_id": "uuid" }
```
### POST `/api/control/unit/{unit_id}/stop-auto`
停止自动控制循环。设备当前状态保持不变,不会自动停机。
```json
{ "ok_msg": "Auto control stopped", "unit_id": "uuid" }
```
### POST `/api/control/unit/{unit_id}/ack-fault`
人工确认故障,解除故障锁定。要求:`fault_locked = true` 且 `flt_active = false`(故障信号已消失)。
```json
{ "ok_msg": "Fault acknowledged", "unit_id": "uuid" }
```
---
## WebSocket
### 连接地址
- 公共广播:`/ws/public`
- 客户端专属:`/ws/client/{client_id}`
### 服务端主动推送消息
#### `PointNewValue`
点位实时值更新:
```json
{
"type": "PointNewValue",
"data": {
"point_id": "uuid",
"timestamp": "2026-03-20 10:05:00.000",
"quality": "good",
"value": 12.3,
"value_type": "float",
"value_text": "12.3"
}
}
```
#### `EventCreated`
系统事件创建(控制操作、故障、状态变更等):
```json
{
"type": "EventCreated",
"data": {
"id": "uuid",
"event_type": "equipment.start_command_sent",
"level": "info",
"unit_id": "uuid",
"equipment_id": "uuid",
"message": "...",
"created_at": "2026-03-25 10:00:00.000"
}
}
```
#### `UnitRuntimeChanged`
控制单元运行时状态变更(每 500ms tick 后广播):
```json
{
"type": "UnitRuntimeChanged",
"data": {
"unit_id": "uuid",
"state": "running",
"auto_enabled": true,
"fault_locked": false,
"comm_locked": false,
"manual_ack_required": false,
"accumulated_run_sec": 3600000
}
}
```
#### `PointSetValueBatchResult`
批量写点结果回调:
```json
{
"type": "PointSetValueBatchResult",
"data": {
"success": true,
"err_msg": null,
"success_count": 1,
"failed_count": 0,
"results": []
}
}
```
### 客户端发送消息
#### 写权限认证
```json
{
"type": "auth_write",
"data": { "key": "your-write-key" }
}
```
#### 批量写点
```json
{
"type": "point_set_value_batch",
"data": {
"items": [
{ "point_id": "uuid", "value": 12.3 }
]
}
}
```
---
## 备注
- 运行时状态(`/runtime`)存储在内存中,服务重启后重置。
- 历史曲线数据(`/history`)同样是内存环形缓冲,重启后清空。
- 控制单元时间配置字段(`run_time_sec` 等)单位为秒,运行时 elapsed 字段单位为毫秒。
- 自动控制启动后,状态机以 500ms 为周期运行,实时状态通过 WebSocket `UnitRuntimeChanged` 推送。