plc_control/API.md

8.2 KiB
Raw Blame History

PLC Control 接口说明

本文档基于当前服务端路由与处理器代码整理,覆盖 HTTP API、SSE 日志流和 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"
}

响应:

{
  "ok_msg": "Source updated successfully"
}

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

获取指定数据源的节点树。

响应字段:

  • 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:每页条数

响应示例:

{
  "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

响应示例:

[
  {
    "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}

更新点位元数据。

请求体:

{
  "name": "Temperature",
  "description": "Room temperature",
  "unit": "°C",
  "tag_id": "uuid"
}

DELETE /api/point/{point_id}

删除单个点位。

响应:

{
  "ok_msg": "Point deleted successfully"
}

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"]
}

响应:

{
  "deleted_count": 2
}

PUT /api/point/batch/set-tags

批量设置点位标签。

请求体:

{
  "point_ids": ["uuid1", "uuid2"],
  "tag_id": "uuid"
}

POST /api/point/value/batch

批量写点。

请求头:

  • X-Write-Key: <key>

请求体:

{
  "items": [
    {
      "point_id": "uuid",
      "value": 12.3
    }
  ]
}

响应:

{
  "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

创建标签。

请求体:

{
  "name": "Area-A",
  "description": "Area A points",
  "point_ids": ["uuid1", "uuid2"]
}

PUT /api/tag/{tag_id}

更新标签。

请求体:

{
  "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

创建页面。

请求体:

{
  "name": "Dashboard",
  "data": {
    "widgetA": "uuid1",
    "widgetB": "uuid2"
  }
}

PUT /api/page/{page_id}

更新页面。

请求体字段均可选:

{
  "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:可选

响应:

{
  "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

{
  "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

{
  "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
      }
    ]
  }
}

备注

  • 历史曲线接口当前使用内存缓存,服务重启后历史会清空。
  • 实时遥测与 WebSocket 推送是“最新值优先”的设计,在高压场景下允许丢弃部分中间消息。
  • /api/tag/{tag_id} 当前返回的是标签下点位,而不是标签自身详情。