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消息:
心跳检测
{
"type": "ping",
"timestamp": "2025-06-11T12:00:00.000Z"
}
获取当前状态
{
"type": "get_status",
"timestamp": "2025-06-11T12:00:00.000Z"
}
服务器消息格式
任务执行结果更新
{
"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"
}
]
}
心跳响应
{
"type": "pong",
"timestamp": "2025-06-11T12:00:00.000Z"
}
错误消息
{
"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客户端示例
// 建立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客户端示例
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())
特性说明
- 智能推送:服务器只在数据发生变化时才推送更新,避免不必要的网络流量
- 心跳检测:支持客户端主动发送心跳包,维持连接活跃状态
- 错误处理:完善的错误处理机制,连接异常时自动清理资源
- 状态查询:客户端可随时主动请求获取当前任务状态
- 多客户端支持:同一任务记录可支持多个客户端同时连接
2. 任务执行结果广播
接口说明
建立WebSocket连接,接收任务执行结果的广播消息。与实时推送接口的区别在于,此接口主要用于被动接收广播,不会主动定期推送。
连接路径
ws://your-domain/ws/task-execution-broadcast/{task_record_id}
路径参数
参数名 |
类型 |
是否必须 |
描述 |
task_record_id |
string |
是 |
任务记录ID |
客户端消息格式
心跳检测
{
"type": "ping",
"timestamp": "2025-06-11T12:00:00.000Z"
}
服务器消息格式
与任务执行结果实时推送接口相同,参见上述文档。
使用场景
- 监控面板:多个监控客户端同时监听任务状态变化
- 日志收集:收集任务执行过程中的状态变化记录
- 事件通知:当任务状态发生变化时接收通知
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消息:
心跳检测
{
"type": "ping",
"timestamp": "2025-06-11T12:00:00.000Z"
}
获取当前状态
{
"type": "get_status",
"timestamp": "2025-06-11T12:00:00.000Z"
}
服务器消息格式
库位状态更新
{
"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"
}
]
}
}
库位状态变化通知
{
"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"
}
}
心跳响应
{
"type": "pong",
"timestamp": "2025-06-11T12:00:00.000Z"
}
错误消息
{
"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客户端示例
// 建立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客户端示例
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())
特性说明
- 智能推送:服务器只在数据发生变化时才推送更新,避免不必要的网络流量
- 灵活过滤:支持多种过滤条件,可以精确筛选需要监控的库位
- 心跳检测:支持客户端主动发送心跳包,维持连接活跃状态
- 错误处理:完善的错误处理机制,连接异常时自动清理资源
- 状态查询:客户端可随时主动请求获取当前库位状态
- 多客户端支持:同一场景可支持多个客户端同时连接
4. 库位状态广播
接口说明
建立WebSocket连接,接收库位状态的广播消息。与实时推送接口的区别在于,此接口主要用于被动接收广播,不会主动定期推送。
连接路径
ws://your-domain/ws/storage-location-broadcast/{scene_id}
路径参数
参数名 |
类型 |
是否必须 |
描述 |
scene_id |
string |
是 |
场景ID |
客户端消息格式
心跳检测
{
"type": "ping",
"timestamp": "2025-06-11T12:00:00.000Z"
}
服务器消息格式
与库位状态实时推送接口相同,参见上述文档。
使用场景
- 监控面板:多个监控客户端同时监听库位状态变化
- 库位管理:实时显示库位占用、锁定状态
- 货物追踪:监控货物存放和取出过程
- 状态统计:收集库位使用率和状态变化统计
错误码说明
错误码 |
描述 |
解决方案 |
1006 |
连接异常关闭 |
检查网络连接,重新建立连接 |
1011 |
服务器内部错误 |
检查服务器状态和日志 |
1013 |
临时服务不可用 |
稍后重试连接 |
最佳实践
1. 连接管理
- 实现连接断开后的自动重连机制
- 合理设置推送间隔,避免过于频繁的请求
- 及时关闭不需要的连接,释放服务器资源
2. 错误处理
- 监听
onerror
和onclose
事件,处理连接异常
- 实现重连退避策略,避免连接风暴
- 记录错误日志,便于问题排查
3. 性能优化
- 使用合适的推送间隔(任务执行结果建议2-5秒,库位状态建议3-10秒)
- 客户端及时处理接收到的消息,避免消息积压
- 对于不活跃的任务,考虑降低推送频率
- 库位状态推送时,合理使用过滤条件,避免获取过多不必要的数据
- 对于大规模库位监控,考虑按库区分组建立多个连接
4. 安全考虑
- 在生产环境中使用WSS协议(WebSocket Secure)
- 实现适当的身份验证和授权机制
- 限制连接数量,防止资源滥用
- 对于库位状态推送,验证客户端是否有权限访问特定场景的库位数据
注意事项
- ID有效性:确保传入的任务记录ID和场景ID存在且有效
- 网络稳定性:WebSocket连接对网络质量要求较高,不稳定的网络可能导致频繁断连
- 浏览器兼容性:确保目标浏览器支持WebSocket协议
- 资源清理:页面关闭或组件销毁时,及时关闭WebSocket连接
- 消息处理:合理处理接收到的消息,避免阻塞UI线程
- 过滤条件:库位状态推送时,合理设置过滤条件,避免获取过多数据影响性能
- 数据更新频率:库位状态数据更新频率可能较高,建议根据实际需求调整推送间隔
- 并发连接:避免对同一场景建立过多并发连接,建议复用连接或使用广播接口
更新日志
版本 |
日期 |
更新内容 |
1.0.0 |
2025-06-11 |
初始版本,支持任务执行结果实时推送和广播功能 |
1.1.0 |
2025-06-11 |
新增库位状态实时推送和广播功能,支持多种过滤条件和状态变化通知 |