VWED_server/VWED任务模块接口文档/WebSocket接口文档.md

639 lines
19 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.

# VWED WebSocket接口文档
本文档描述了VWED系统WebSocket相关的API接口主要用于实时推送任务执行结果和状态更新。
## 基础信息
- 基础路径:`/ws`
- 接口标签:`WebSocket`
- 协议WebSocket协议ws://或wss://
## 接口清单
| 序号 | 接口名称 | 接口路径 | 协议 | 接口描述 |
| --- | --- | --- | --- | --- |
| 1 | 任务执行结果实时推送 | `/task-execution/{task_record_id}` | WebSocket | 实时推送指定任务记录的执行结果更新 |
| 2 | 任务执行结果广播 | `/task-execution-broadcast/{task_record_id}` | WebSocket | 接收任务执行结果广播消息 |
| 3 | 库位状态实时推送 | `/storage-location/{scene_id}` | WebSocket | 实时推送指定场景的库位状态更新 |
| 4 | 库位状态广播 | `/storage-location-broadcast/{scene_id}` | WebSocket | 接收库位状态广播消息 |
## 接口详情
### 1. 任务执行结果实时推送
#### 接口说明
建立WebSocket连接实时接收指定任务记录的执行结果更新。服务器会定期推送任务状态变化客户端也可以主动请求获取当前状态。
#### 连接路径
```
ws://your-domain/ws/task-execution/{task_record_id}?interval={interval}
```
#### 路径参数
| 参数名 | 类型 | 是否必须 | 描述 |
| --- | --- | --- | --- |
| task_record_id | string | 是 | 任务记录ID |
#### 查询参数
| 参数名 | 类型 | 是否必须 | 默认值 | 描述 |
| --- | --- | --- | --- | --- |
| interval | integer | 否 | 2 | 推送间隔范围1-30秒 |
#### 客户端消息格式
客户端可以向服务器发送以下格式的JSON消息
##### 心跳检测
```json
{
"type": "ping",
"timestamp": "2025-06-11T12:00:00.000Z"
}
```
##### 获取当前状态
```json
{
"type": "get_status",
"timestamp": "2025-06-11T12:00:00.000Z"
}
```
#### 服务器消息格式
##### 任务执行结果更新
```json
{
"type": "task_execution_update",
"task_record_id": "任务记录ID",
"timestamp": "2025-06-11T12:00:00.000Z",
"message": "成功获取任务记录执行结果",
"data": [
{
"created_at": "2025-06-11T12:00:00.000Z",
"context": "[块执行名称] 执行内容描述",
"status": "SUCCESS/FAILED/RUNNING"
}
]
}
```
##### 心跳响应
```json
{
"type": "pong",
"timestamp": "2025-06-11T12:00:00.000Z"
}
```
##### 错误消息
```json
{
"type": "error",
"task_record_id": "任务记录ID",
"timestamp": "2025-06-11T12:00:00.000Z",
"message": "错误描述信息"
}
```
#### 响应字段说明
##### 任务执行结果字段
| 字段名 | 类型 | 描述 |
| --- | --- | --- |
| type | string | 消息类型,固定为"task_execution_update" |
| task_record_id | string | 任务记录ID |
| timestamp | string | 消息时间戳ISO 8601格式 |
| message | string | 响应消息描述 |
| data | array | 执行结果数组 |
| data[].created_at | string | 结果创建时间ISO 8601格式 |
| data[].context | string | 执行内容描述 |
| data[].status | string | 执行状态SUCCESS成功、FAILED失败、RUNNING执行中 |
#### 连接示例
##### JavaScript客户端示例
```javascript
// 建立WebSocket连接
const taskRecordId = "your-task-record-id";
const interval = 2; // 推送间隔2秒
const wsUrl = `ws://localhost:8000/ws/task-execution/${taskRecordId}?interval=${interval}`;
const websocket = new WebSocket(wsUrl);
// 连接建立
websocket.onopen = function(event) {
console.log("WebSocket连接已建立");
// 发送心跳包
websocket.send(JSON.stringify({
type: "ping",
timestamp: new Date().toISOString()
}));
};
// 接收消息
websocket.onmessage = function(event) {
const data = JSON.parse(event.data);
switch(data.type) {
case "task_execution_update":
console.log("任务执行结果更新:", data.data);
break;
case "pong":
console.log("心跳响应:", data.timestamp);
break;
case "error":
console.error("服务器错误:", data.message);
break;
}
};
// 连接关闭
websocket.onclose = function(event) {
console.log("WebSocket连接已关闭");
};
// 连接错误
websocket.onerror = function(error) {
console.error("WebSocket连接错误:", error);
};
```
##### Python客户端示例
```python
import asyncio
import json
import websockets
async def websocket_client():
task_record_id = "your-task-record-id"
interval = 2
uri = f"ws://localhost:8000/ws/task-execution/{task_record_id}?interval={interval}"
async with websockets.connect(uri) as websocket:
print("WebSocket连接已建立")
# 发送心跳包
await websocket.send(json.dumps({
"type": "ping",
"timestamp": datetime.now().isoformat()
}))
# 监听消息
async for message in websocket:
data = json.loads(message)
if data["type"] == "task_execution_update":
print(f"任务执行结果更新: {data['data']}")
elif data["type"] == "pong":
print(f"心跳响应: {data['timestamp']}")
elif data["type"] == "error":
print(f"服务器错误: {data['message']}")
# 运行客户端
asyncio.run(websocket_client())
```
#### 特性说明
1. **智能推送**:服务器只在数据发生变化时才推送更新,避免不必要的网络流量
2. **心跳检测**:支持客户端主动发送心跳包,维持连接活跃状态
3. **错误处理**:完善的错误处理机制,连接异常时自动清理资源
4. **状态查询**:客户端可随时主动请求获取当前任务状态
5. **多客户端支持**:同一任务记录可支持多个客户端同时连接
### 2. 任务执行结果广播
#### 接口说明
建立WebSocket连接接收任务执行结果的广播消息。与实时推送接口的区别在于此接口主要用于被动接收广播不会主动定期推送。
#### 连接路径
```
ws://your-domain/ws/task-execution-broadcast/{task_record_id}
```
#### 路径参数
| 参数名 | 类型 | 是否必须 | 描述 |
| --- | --- | --- | --- |
| task_record_id | string | 是 | 任务记录ID |
#### 客户端消息格式
##### 心跳检测
```json
{
"type": "ping",
"timestamp": "2025-06-11T12:00:00.000Z"
}
```
#### 服务器消息格式
与任务执行结果实时推送接口相同,参见上述文档。
#### 使用场景
1. **监控面板**:多个监控客户端同时监听任务状态变化
2. **日志收集**:收集任务执行过程中的状态变化记录
3. **事件通知**:当任务状态发生变化时接收通知
### 3. 库位状态实时推送
#### 接口说明
建立WebSocket连接实时接收指定场景的库位状态更新。服务器会定期推送库位状态变化客户端也可以主动请求获取当前状态。支持多种过滤条件来筛选特定的库位。
#### 连接路径
```
ws://your-domain/ws/storage-location/{scene_id}?interval={interval}&storage_area_id={storage_area_id}&station_name={station_name}&layer_name={layer_name}&is_occupied={is_occupied}&is_locked={is_locked}&is_disabled={is_disabled}
```
#### 路径参数
| 参数名 | 类型 | 是否必须 | 描述 |
| --- | --- | --- | --- |
| scene_id | string | 是 | 场景ID |
#### 查询参数
| 参数名 | 类型 | 是否必须 | 默认值 | 描述 |
| --- | --- | --- | --- | --- |
| interval | integer | 否 | 3 | 推送间隔范围1-30秒 |
| storage_area_id | string | 否 | null | 库区ID用于过滤特定库区 |
| station_name | string | 否 | null | 站点名称,用于过滤特定站点 |
| layer_name | string | 否 | null | 层名称,用于过滤特定层 |
| is_occupied | boolean | 否 | null | 是否占用过滤 |
| is_locked | boolean | 否 | null | 是否锁定过滤 |
| is_disabled | boolean | 否 | null | 是否禁用过滤 |
#### 客户端消息格式
客户端可以向服务器发送以下格式的JSON消息
##### 心跳检测
```json
{
"type": "ping",
"timestamp": "2025-06-11T12:00:00.000Z"
}
```
##### 获取当前状态
```json
{
"type": "get_status",
"timestamp": "2025-06-11T12:00:00.000Z"
}
```
#### 服务器消息格式
##### 库位状态更新
```json
{
"type": "storage_location_update",
"scene_id": "场景ID",
"timestamp": "2025-06-11T12:00:00.000Z",
"message": "成功获取库位状态",
"data": {
"total": 100,
"page": 1,
"page_size": 1000,
"total_pages": 1,
"storage_locations": [
{
"id": "层ID",
"layer_index": 1,
"layer_name": "层名称",
"operate_point_id": "动作点ID",
"station_name": "站点名称",
"storage_location_name": "库位名称",
"scene_id": "场景ID",
"storage_area_id": "库区ID",
"area_name": "库区名称",
"is_occupied": false,
"is_locked": false,
"is_disabled": false,
"is_empty_tray": false,
"locked_by": null,
"goods_content": "",
"goods_weight": null,
"goods_volume": null,
"goods_stored_at": null,
"goods_retrieved_at": null,
"last_access_at": "2025-06-11T12:00:00.000Z",
"max_weight": 5000,
"max_volume": 1000,
"layer_height": 100,
"tags": "",
"description": null,
"created_at": "2025-06-11T12:00:00.000Z",
"updated_at": "2025-06-11T12:00:00.000Z"
}
]
}
}
```
##### 库位状态变化通知
```json
{
"type": "storage_location_status_change",
"scene_id": "场景ID",
"layer_name": "层名称",
"action": "OCCUPY",
"timestamp": "2025-06-11T12:00:00.000Z",
"new_status": {
"id": "层ID",
"is_occupied": true,
"is_locked": false,
"is_disabled": false,
"is_empty_tray": false,
"locked_by": null,
"goods_content": "货物内容",
"last_access_at": "2025-06-11T12:00:00.000Z",
"updated_at": "2025-06-11T12:00:00.000Z"
}
}
```
##### 心跳响应
```json
{
"type": "pong",
"timestamp": "2025-06-11T12:00:00.000Z"
}
```
##### 错误消息
```json
{
"type": "error",
"scene_id": "场景ID",
"timestamp": "2025-06-11T12:00:00.000Z",
"message": "错误描述信息"
}
```
#### 响应字段说明
##### 库位状态字段
| 字段名 | 类型 | 描述 |
| --- | --- | --- |
| id | string | 层ID |
| layer_index | integer | 层索引(从1开始) |
| layer_name | string | 层名称 |
| operate_point_id | string | 动作点ID |
| station_name | string | 站点名称 |
| storage_location_name | string | 库位名称 |
| scene_id | string | 场景ID |
| storage_area_id | string | 库区ID |
| area_name | string | 库区名称 |
| is_occupied | boolean | 是否占用 |
| is_locked | boolean | 是否锁定 |
| is_disabled | boolean | 是否禁用 |
| is_empty_tray | boolean | 是否空托盘 |
| locked_by | string | 锁定者 |
| goods_content | string | 货物内容 |
| goods_weight | integer | 货物重量(克) |
| goods_volume | integer | 货物体积(立方厘米) |
| goods_stored_at | string | 货物存放时间 |
| goods_retrieved_at | string | 货物取出时间 |
| last_access_at | string | 最后访问时间 |
| max_weight | integer | 最大承重(克) |
| max_volume | integer | 最大体积(立方厘米) |
| layer_height | integer | 层高(毫米) |
| tags | string | 标签 |
| description | string | 层描述 |
| created_at | string | 创建时间 |
| updated_at | string | 更新时间 |
#### 连接示例
##### JavaScript客户端示例
```javascript
// 建立WebSocket连接
const sceneId = "your-scene-id";
const interval = 3; // 推送间隔3秒
const storageAreaId = "area-001"; // 过滤特定库区
const wsUrl = `ws://localhost:8000/ws/storage-location/${sceneId}?interval=${interval}&storage_area_id=${storageAreaId}&is_occupied=false`;
const websocket = new WebSocket(wsUrl);
// 连接建立
websocket.onopen = function(event) {
console.log("库位状态WebSocket连接已建立");
// 发送心跳包
websocket.send(JSON.stringify({
type: "ping",
timestamp: new Date().toISOString()
}));
};
// 接收消息
websocket.onmessage = function(event) {
const data = JSON.parse(event.data);
switch(data.type) {
case "storage_location_update":
console.log("库位状态更新:", data.data);
// 处理库位状态列表
data.data.storage_locations.forEach(location => {
console.log(`层${location.layer_name}: 占用=${location.is_occupied}, 锁定=${location.is_locked}`);
});
break;
case "storage_location_status_change":
console.log("库位状态变化:", data.layer_name, data.action, data.new_status);
break;
case "pong":
console.log("心跳响应:", data.timestamp);
break;
case "error":
console.error("服务器错误:", data.message);
break;
}
};
// 连接关闭
websocket.onclose = function(event) {
console.log("库位状态WebSocket连接已关闭");
};
// 连接错误
websocket.onerror = function(error) {
console.error("库位状态WebSocket连接错误:", error);
};
```
##### Python客户端示例
```python
import asyncio
import json
import websockets
from datetime import datetime
async def storage_location_websocket_client():
scene_id = "your-scene-id"
interval = 3
storage_area_id = "area-001"
uri = f"ws://localhost:8000/ws/storage-location/{scene_id}?interval={interval}&storage_area_id={storage_area_id}&is_occupied=false"
async with websockets.connect(uri) as websocket:
print("库位状态WebSocket连接已建立")
# 发送心跳包
await websocket.send(json.dumps({
"type": "ping",
"timestamp": datetime.now().isoformat()
}))
# 监听消息
async for message in websocket:
data = json.loads(message)
if data["type"] == "storage_location_update":
print(f"库位状态更新: 共{data['data']['total']}个库位")
for location in data["data"]["storage_locations"]:
print(f" 层{location['layer_name']}: 占用={location['is_occupied']}, 锁定={location['is_locked']}")
elif data["type"] == "storage_location_status_change":
print(f"库位状态变化: {data['layer_name']} {data['action']} {data['new_status']}")
elif data["type"] == "pong":
print(f"心跳响应: {data['timestamp']}")
elif data["type"] == "error":
print(f"服务器错误: {data['message']}")
# 运行客户端
asyncio.run(storage_location_websocket_client())
```
#### 特性说明
1. **智能推送**:服务器只在数据发生变化时才推送更新,避免不必要的网络流量
2. **灵活过滤**:支持多种过滤条件,可以精确筛选需要监控的库位
3. **心跳检测**:支持客户端主动发送心跳包,维持连接活跃状态
4. **错误处理**:完善的错误处理机制,连接异常时自动清理资源
5. **状态查询**:客户端可随时主动请求获取当前库位状态
6. **多客户端支持**:同一场景可支持多个客户端同时连接
### 4. 库位状态广播
#### 接口说明
建立WebSocket连接接收库位状态的广播消息。与实时推送接口的区别在于此接口主要用于被动接收广播不会主动定期推送。
#### 连接路径
```
ws://your-domain/ws/storage-location-broadcast/{scene_id}
```
#### 路径参数
| 参数名 | 类型 | 是否必须 | 描述 |
| --- | --- | --- | --- |
| scene_id | string | 是 | 场景ID |
#### 客户端消息格式
##### 心跳检测
```json
{
"type": "ping",
"timestamp": "2025-06-11T12:00:00.000Z"
}
```
#### 服务器消息格式
与库位状态实时推送接口相同,参见上述文档。
#### 使用场景
1. **监控面板**:多个监控客户端同时监听库位状态变化
2. **库位管理**:实时显示库位占用、锁定状态
3. **货物追踪**:监控货物存放和取出过程
4. **状态统计**:收集库位使用率和状态变化统计
## 错误码说明
| 错误码 | 描述 | 解决方案 |
| --- | --- | --- |
| 1006 | 连接异常关闭 | 检查网络连接,重新建立连接 |
| 1011 | 服务器内部错误 | 检查服务器状态和日志 |
| 1013 | 临时服务不可用 | 稍后重试连接 |
## 最佳实践
### 1. 连接管理
- 实现连接断开后的自动重连机制
- 合理设置推送间隔,避免过于频繁的请求
- 及时关闭不需要的连接,释放服务器资源
### 2. 错误处理
- 监听`onerror``onclose`事件,处理连接异常
- 实现重连退避策略,避免连接风暴
- 记录错误日志,便于问题排查
### 3. 性能优化
- 使用合适的推送间隔任务执行结果建议2-5秒库位状态建议3-10秒
- 客户端及时处理接收到的消息,避免消息积压
- 对于不活跃的任务,考虑降低推送频率
- 库位状态推送时,合理使用过滤条件,避免获取过多不必要的数据
- 对于大规模库位监控,考虑按库区分组建立多个连接
### 4. 安全考虑
- 在生产环境中使用WSS协议WebSocket Secure
- 实现适当的身份验证和授权机制
- 限制连接数量,防止资源滥用
- 对于库位状态推送,验证客户端是否有权限访问特定场景的库位数据
## 注意事项
1. **ID有效性**确保传入的任务记录ID和场景ID存在且有效
2. **网络稳定性**WebSocket连接对网络质量要求较高不稳定的网络可能导致频繁断连
3. **浏览器兼容性**确保目标浏览器支持WebSocket协议
4. **资源清理**页面关闭或组件销毁时及时关闭WebSocket连接
5. **消息处理**合理处理接收到的消息避免阻塞UI线程
6. **过滤条件**:库位状态推送时,合理设置过滤条件,避免获取过多数据影响性能
7. **数据更新频率**:库位状态数据更新频率可能较高,建议根据实际需求调整推送间隔
8. **并发连接**:避免对同一场景建立过多并发连接,建议复用连接或使用广播接口
## 更新日志
| 版本 | 日期 | 更新内容 |
| --- | --- | --- |
| 1.0.0 | 2025-06-11 | 初始版本,支持任务执行结果实时推送和广播功能 |
| 1.1.0 | 2025-06-11 | 新增库位状态实时推送和广播功能,支持多种过滤条件和状态变化通知 |