16 KiB
PLC Control 接口说明
本文档基于当前服务端路由与处理器代码整理,覆盖 HTTP API 和 WebSocket 实时消息。
基本信息
- 服务端默认提供静态 UI:
/ui - HTTP API 前缀:
/api - 公共实时 WebSocket:
/ws/public - 客户端专属 WebSocket:
/ws/client/{client_id}
通用错误响应
接口失败时通常返回:
{
"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
获取所有已启用数据源及其连接状态。
响应示例:
[
{
"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
创建数据源。
请求体:
{
"name": "PLC-1",
"endpoint": "opc.tcp://127.0.0.1:4840",
"enabled": true
}
响应:
{ "id": "uuid" }
PUT /api/source/{source_id}
更新数据源。请求体字段均可选:
{
"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
手动重连指定数据源。
{ "ok_msg": "Source reconnected successfully" }
POST /api/source/{source_id}/browse
从 OPC UA 源浏览节点并写入本地 node 表。
{ "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表示全量)
响应示例:
{
"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)
[
{
"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}
更新点位元数据,字段均可选:
{
"name": "Temperature",
"description": "Room temperature",
"unit": "°C",
"equipment_id": "uuid",
"signal_role": "run"
}
DELETE /api/point/{point_id}
删除单个点位。
POST /api/point/batch
根据节点批量创建点位。
{ "node_ids": ["uuid1", "uuid2"] }
响应:
{
"success_count": 2,
"failed_count": 0,
"failed_node_ids": [],
"created_point_ids": ["uuid3", "uuid4"]
}
DELETE /api/point/batch
批量删除点位。
{ "point_ids": ["uuid1", "uuid2"] }
PUT /api/point/batch/set-equipment
批量设置点位的设备绑定和信号角色。
{
"point_ids": ["uuid1", "uuid2"],
"equipment_id": "uuid",
"signal_role": "run"
}
PUT /api/point/batch/set-tags
批量设置点位的标签(tag_id,传 null 可清除绑定)。
{
"point_ids": ["uuid1", "uuid2"],
"tag_id": "uuid"
}
响应:
{ "ok_msg": "Point tags updated successfully", "updated_count": 2 }
POST /api/point/value/batch
批量写点。
请求头:X-Write-Key: <key>
{
"items": [
{ "point_id": "uuid", "value": 12.3 }
]
}
Equipment
GET /api/equipment
分页获取设备列表,包含每台设备绑定的点位数量。
查询参数:page、page_size(-1 全量)、keyword(可选,按 code/name 模糊搜索)
响应示例:
{
"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
创建设备。
{
"unit_id": "uuid",
"code": "E01",
"name": "投煤器1",
"kind": "coal_feeder",
"description": null
}
响应:201 Created
{ "id": "uuid", "ok_msg": "Equipment created successfully" }
PUT /api/equipment/{equipment_id}
更新设备,字段均可选:
{
"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
批量将设备绑定到控制单元。
{
"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
创建控制单元。
{
"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
{ "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
获取控制单元的当前运行时状态(内存中,不持久化)。
响应示例:
{
"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
获取控制单元及其下所有设备和点位的完整嵌套结构。
响应示例:
{
"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
响应示例:
{
"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)。
响应示例:
{
"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)。
{ "ok_msg": "Auto control started", "unit_id": "uuid" }
POST /api/control/unit/{unit_id}/stop-auto
停止自动控制循环。设备当前状态保持不变,不会自动停机。
{ "ok_msg": "Auto control stopped", "unit_id": "uuid" }
POST /api/control/unit/batch-start-auto
批量启动所有已启用(enabled = true)控制单元的自动控制。已在运行、故障锁或通讯锁的单元将跳过。
{
"started": ["uuid1", "uuid2"],
"skipped": ["uuid3"]
}
POST /api/control/unit/batch-stop-auto
批量停止所有自动控制中的控制单元。
{ "stopped": ["uuid1", "uuid2"] }
POST /api/control/unit/{unit_id}/ack-fault
人工确认故障,解除故障锁定。要求:fault_locked = true 且 flt_active = false(故障信号已消失)。
{ "ok_msg": "Fault acknowledged", "unit_id": "uuid" }
Tag(标签)
GET /api/tag
分页获取标签列表。
查询参数:page、page_size
响应示例:
{
"data": [
{
"id": "uuid",
"name": "主蒸汽",
"description": null,
"created_at": "2026-03-20 10:00:00.000",
"updated_at": "2026-03-20 10:00:00.000"
}
],
"total": 1,
"page": 1,
"page_size": 20
}
POST /api/tag
创建标签,可同时绑定点位。
{
"name": "主蒸汽",
"description": null,
"point_ids": ["uuid1", "uuid2"]
}
响应:201 Created
{ "id": "uuid", "ok_msg": "Tag created successfully" }
GET /api/tag/{tag_id}
获取标签下所有绑定点位。
响应:点位对象数组。
PUT /api/tag/{tag_id}
更新标签,字段均可选(point_ids 传入时全量替换绑定关系):
{
"name": "主蒸汽",
"description": "描述",
"point_ids": ["uuid1"]
}
DELETE /api/tag/{tag_id}
删除标签。成功响应:204 No Content
Page(自定义页面)
GET /api/page
获取所有页面,按 created_at 排序。
查询参数:name(可选,模糊搜索)
响应:Page 对象数组。
[
{
"id": "uuid",
"name": "总览",
"data": { "slot_key": "point-uuid" },
"created_at": "2026-03-20 10:00:00.000",
"updated_at": "2026-03-20 10:00:00.000"
}
]
data 为 { slot_key: point_id } 映射,用于页面布局与点位绑定。
POST /api/page
创建页面。
{
"name": "总览",
"data": { "slot_a": "uuid1", "slot_b": "uuid2" }
}
响应:201 Created
{ "id": "uuid", "ok_msg": "Page created successfully" }
GET /api/page/{page_id}
获取单个页面。
PUT /api/page/{page_id}
更新页面,字段均可选:
{
"name": "总览",
"data": { "slot_a": "uuid1" }
}
DELETE /api/page/{page_id}
删除页面。成功响应:204 No Content
Log(运行日志)
GET /api/logs
读取服务端日志文件内容(默认取最新 app.log* 文件尾部 200 行)。
查询参数:
file:可选,指定文件名(仅允许app.log前缀)cursor:可选,上次返回的字节偏移量;传入时增量读取 cursor 之后的内容tail_lines:可选,不传 cursor 时返回的尾部行数(默认 200,最大 2000)max_bytes:可选,单次最多返回字节数(默认 64 KB,最大 512 KB)
响应示例:
{
"file": "app.log",
"cursor": 204800,
"lines": ["2026-03-25 10:00:00 INFO ..."],
"truncated": false,
"reset": false
}
truncated:true表示本次未读完,可用新 cursor 继续请求reset:true表示文件已轮转(cursor > 文件大小),已从头读取
GET /api/logs/stream
以 SSE(Server-Sent Events)流式推送日志增量,每 800 ms 检查一次文件变化。
查询参数:file、cursor(可选,默认从文件末尾开始)、max_bytes(默认 32 KB)
事件格式:
event: log
data: { "file": "app.log", "cursor": 204800, "lines": [...], "truncated": false, "reset": false }
event: error
data: log stream read failed
WebSocket
连接地址
- 公共广播:
/ws/public - 客户端专属:
/ws/client/{client_id}
服务端主动推送消息
PointNewValue
点位实时值更新:
{
"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
系统事件创建(控制操作、故障、状态变更等):
{
"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 后广播):
{
"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
批量写点结果回调:
{
"type": "PointSetValueBatchResult",
"data": {
"success": true,
"err_msg": null,
"success_count": 1,
"failed_count": 0,
"results": []
}
}
客户端发送消息
写权限认证
{
"type": "auth_write",
"data": { "key": "your-write-key" }
}
批量写点
{
"type": "point_set_value_batch",
"data": {
"items": [
{ "point_id": "uuid", "value": 12.3 }
]
}
}
备注
- 运行时状态(
/runtime)存储在内存中,服务重启后重置。 - 历史曲线数据(
/history)同样是内存环形缓冲,重启后清空。 - 控制单元时间配置字段(
run_time_sec等)单位为秒,运行时 elapsed 字段单位为毫秒。 - 自动控制启动后,状态机以 500ms 为周期运行,实时状态通过 WebSocket
UnitRuntimeChanged推送。