plc_control/API.md

591 lines
8.2 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、SSE 日志流和 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"
}
```
响应:
```json
{
"ok_msg": "Source updated successfully"
}
```
### 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`
获取指定数据源的节点树。
响应字段:
- `id`
- `source_id`
- `external_id`
- `namespace_uri`
- `namespace_index`
- `identifier_type`
- `identifier`
- `browse_name`
- `display_name`
- `node_class`
- `parent_id`
- `children`
---
## Point
### GET `/api/point`
分页获取点位列表。
查询参数:
- `source_id`:可选,按数据源过滤
- `page`:页码
- `page_size`:每页条数
响应示例:
```json
{
"data": [
{
"id": "uuid",
"node_id": "uuid",
"name": "Temperature",
"description": null,
"unit": null,
"tag_id": null,
"created_at": "2026-03-20 10:00:00.000",
"updated_at": "2026-03-20 10:00:00.000",
"point_monitor": {
"protocol": "opcua",
"source_id": "uuid",
"point_id": "uuid",
"client_handle": 1001,
"scan_mode": "subscribe",
"timestamp": "2026-03-20 10:05:00.000",
"quality": "good",
"value": 12.3,
"value_type": "float",
"value_text": "12.3",
"old_value": 12.1,
"old_timestamp": "2026-03-20 10:04:59.000",
"value_changed": true
}
}
],
"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
}
]
```
说明:
- `value_number` 便于前端直接绘图
- 非数值型点位时,`value_number` 可能为 `null`
### PUT `/api/point/{point_id}`
更新点位元数据。
请求体:
```json
{
"name": "Temperature",
"description": "Room temperature",
"unit": "°C",
"tag_id": "uuid"
}
```
### DELETE `/api/point/{point_id}`
删除单个点位。
响应:
```json
{
"ok_msg": "Point deleted successfully"
}
```
### 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"]
}
```
响应:
```json
{
"deleted_count": 2
}
```
### PUT `/api/point/batch/set-tags`
批量设置点位标签。
请求体:
```json
{
"point_ids": ["uuid1", "uuid2"],
"tag_id": "uuid"
}
```
### POST `/api/point/value/batch`
批量写点。
请求头:
- `X-Write-Key: <key>`
请求体:
```json
{
"items": [
{
"point_id": "uuid",
"value": 12.3
}
]
}
```
响应:
```json
{
"success": true,
"err_msg": null,
"success_count": 1,
"failed_count": 0,
"results": [
{
"point_id": "uuid",
"success": true,
"err_msg": null
}
]
}
```
---
## Tag
### GET `/api/tag`
分页获取标签列表。
查询参数:
- `page`
- `page_size`
### GET `/api/tag/{tag_id}`
当前实现返回该标签下的点位列表。
### POST `/api/tag`
创建标签。
请求体:
```json
{
"name": "Area-A",
"description": "Area A points",
"point_ids": ["uuid1", "uuid2"]
}
```
### PUT `/api/tag/{tag_id}`
更新标签。
请求体:
```json
{
"name": "Area-A",
"description": "Updated",
"point_ids": ["uuid1", "uuid2"]
}
```
### DELETE `/api/tag/{tag_id}`
删除标签。
成功响应:`204 No Content`
---
## Page
`page` 用于保存页面布局或组件映射数据。
### GET `/api/page`
查询页面列表。
查询参数:
- `name`:可选,按名称模糊搜索
### GET `/api/page/{page_id}`
获取单个页面。
### POST `/api/page`
创建页面。
请求体:
```json
{
"name": "Dashboard",
"data": {
"widgetA": "uuid1",
"widgetB": "uuid2"
}
}
```
### PUT `/api/page/{page_id}`
更新页面。
请求体字段均可选:
```json
{
"name": "Dashboard",
"data": {
"widgetA": "uuid1"
}
}
```
### DELETE `/api/page/{page_id}`
删除页面。
成功响应:`204 No Content`
---
## Log
### GET `/api/logs`
读取日志文件内容。
查询参数:
- `file`:可选,指定日志文件名,仅允许 `app.log*`
- `cursor`:可选,从指定游标后读取
- `tail_lines`:可选,默认 `200`
- `max_bytes`:可选
响应:
```json
{
"file": "app.log",
"cursor": 1024,
"lines": ["..."],
"truncated": false,
"reset": false
}
```
### GET `/api/logs/stream`
SSE 实时日志流。
事件类型:
- `log`
- `error`
客户端可使用 `EventSource` 订阅。
---
## WebSocket
## 连接地址
- 公共广播:`/ws/public`
- 客户端专属:`/ws/client/{client_id}`
## 服务端主动消息
### `PointNewValue`
```json
{
"type": "PointNewValue",
"data": {
"protocol": "opcua",
"source_id": "uuid",
"point_id": "uuid",
"client_handle": 1001,
"scan_mode": "subscribe",
"timestamp": "2026-03-20 10:05:00.000",
"quality": "good",
"value": 12.3,
"value_type": "float",
"value_text": "12.3",
"old_value": 12.1,
"old_timestamp": "2026-03-20 10:04:59.000",
"value_changed": true
}
}
```
### `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
}
]
}
}
```
---
## 备注
- 历史曲线接口当前使用内存缓存,服务重启后历史会清空。
- 实时遥测与 WebSocket 推送是“最新值优先”的设计,在高压场景下允许丢弃部分中间消息。
- `/api/tag/{tag_id}` 当前返回的是标签下点位,而不是标签自身详情。