568 lines
15 KiB
Markdown
568 lines
15 KiB
Markdown
# 设备处理器模块文档(Python 版本)
|
||
|
||
## 概述
|
||
|
||
设备处理器模块提供了基于 VDA5050 协议的设备管理框架,支持 MQTT 通信,用于监听和处理各类设备(AGV小车、门、电梯等)的消息,并自动转发到指定的 topic。
|
||
|
||
## 核心概念
|
||
|
||
### 设备类型 (DeviceType)
|
||
|
||
系统支持以下设备类型:
|
||
- `vehicle`: 小车/AGV
|
||
- `door`: 门
|
||
- `caller`: 呼叫器
|
||
- `lift`: 电梯
|
||
- `custom`: 自定义设备
|
||
|
||
### 设备品牌 (DeviceBrand)
|
||
|
||
系统支持以下品牌:
|
||
- `huarui`: 华睿
|
||
- `seer`: 仙工
|
||
- `hikrobot`: 海康机器人
|
||
- `standard`: 标准协议
|
||
- `custom`: 自定义品牌
|
||
|
||
### 指令类型 (CommandType)
|
||
|
||
根据设备类型不同,支持的指令类型:
|
||
- **小车设备**:`order`、`state`、`factsheet`、`instantActions`
|
||
- **其他设备**:`instantActions`
|
||
|
||
## API 方法
|
||
|
||
### 注册并运行设备处理器
|
||
|
||
```python
|
||
VWED.device.register_and_run(
|
||
device_ids: List[str],
|
||
device_type: str = "vehicle",
|
||
brand_name: str = "huarui",
|
||
command_type: str = None,
|
||
handler: Callable = None,
|
||
description: str = ""
|
||
)
|
||
```
|
||
|
||
**说明**:批量注册设备处理器,系统会自动:
|
||
1. 根据设备品牌和类型生成 MQTT topics
|
||
2. 订阅相应的 topics
|
||
3. 开始监听消息
|
||
4. 消息到达时自动调用处理函数
|
||
5. 自动转发处理结果
|
||
|
||
**参数**:
|
||
- `device_ids`: List[str] - 设备ID列表(必需)
|
||
- `device_type`: str - 设备类型,默认 "vehicle"
|
||
- `brand_name`: str - 设备品牌,默认 "huarui"
|
||
- `command_type`: str - 指令类型(必需),如 "order"、"state"、"instantActions"
|
||
- `handler`: Callable - 消息处理函数(必需)
|
||
- `description`: str - 设备描述信息
|
||
|
||
**返回值**:
|
||
- List[str] - 成功注册的设备ID列表
|
||
|
||
**处理函数格式**:
|
||
|
||
处理函数接收一个 `DeviceMessage` 对象作为参数,并返回一个字典,指示如何处理消息。
|
||
|
||
```python
|
||
def handler(message):
|
||
"""
|
||
消息处理函数
|
||
|
||
参数:
|
||
message: DeviceMessage对象,包含以下属性:
|
||
- device_id: str - 设备ID
|
||
- device_type: DeviceType - 设备类型
|
||
- topic: str - 接收消息的topic
|
||
- payload: dict - 消息载荷(已解析为字典)
|
||
- timestamp: float - 消息时间戳
|
||
|
||
返回值: dict - 处理结果,支持以下字段:
|
||
{
|
||
"forward": bool, # 是否转发消息(可选,默认True)
|
||
"payload": dict, # 要转发的消息载荷(可选,默认使用原始消息)
|
||
"response_topic": str, # 自定义响应topic(可选)
|
||
"response": dict # 自定义响应消息载荷(可选,需配合response_topic)
|
||
}
|
||
"""
|
||
# 处理逻辑
|
||
return {"forward": True}
|
||
```
|
||
|
||
**返回值格式约束**:
|
||
|
||
| 字段 | 类型 | 必需 | 说明 |
|
||
|------|------|------|------|
|
||
| forward | bool | 否 | 是否转发消息,默认 True |
|
||
| payload | dict | 否 | 转发的消息载荷,默认使用原始消息 |
|
||
| response_topic | str | 否 | 自定义响应的 topic |
|
||
| response | dict | 否 | 自定义响应的消息载荷,需配合 response_topic |
|
||
|
||
**注意事项**:
|
||
- 返回值必须是字典类型
|
||
- `payload` 和 `response` 必须是字典类型
|
||
- 如果提供 `response`,必须同时提供 `response_topic`
|
||
|
||
### 使用示例
|
||
|
||
#### 示例1:批量注册华瑞小车处理 order 指令
|
||
|
||
```python
|
||
def agv_order_processor(message):
|
||
"""处理AGV订单指令"""
|
||
VWED.log.info(f"收到订单: 设备 {message.device_id}")
|
||
VWED.log.info(f"订单内容: {message.payload}")
|
||
|
||
# 提取订单信息
|
||
order_id = message.payload.get("orderId")
|
||
nodes = message.payload.get("nodes", [])
|
||
|
||
# 可以在这里添加业务逻辑,比如:
|
||
# - 验证订单有效性
|
||
# - 记录到数据库
|
||
# - 通知其他系统
|
||
|
||
# 直接透传到设备
|
||
return {
|
||
"forward": True # 使用原始消息转发
|
||
}
|
||
|
||
# 注册处理器
|
||
VWED.device.register_and_run(
|
||
device_ids=["AGV001", "AGV002", "AGV003"],
|
||
device_type="vehicle",
|
||
brand_name="huarui",
|
||
command_type="order",
|
||
handler=agv_order_processor,
|
||
description="华瑞小车order指令处理器"
|
||
)
|
||
```
|
||
|
||
#### 示例2:修改消息后转发
|
||
|
||
```python
|
||
def agv_state_processor(message):
|
||
"""处理AGV状态消息,添加额外信息后转发"""
|
||
state_data = message.payload
|
||
|
||
# 添加额外的业务信息
|
||
enhanced_state = {
|
||
**state_data,
|
||
"processed_at": VWED.get_current_time(),
|
||
"system_status": "online",
|
||
"battery_warning": state_data.get("batteryState", {}).get("batteryCharge", 100) < 20
|
||
}
|
||
|
||
# 转发修改后的消息
|
||
return {
|
||
"forward": True,
|
||
"payload": enhanced_state
|
||
}
|
||
|
||
VWED.device.register_and_run(
|
||
device_ids=["AGV001", "AGV002"],
|
||
device_type="vehicle",
|
||
brand_name="huarui",
|
||
command_type="state",
|
||
handler=agv_state_processor,
|
||
description="AGV状态处理器(增强版)"
|
||
)
|
||
```
|
||
|
||
#### 示例3:不转发,仅记录
|
||
|
||
```python
|
||
def agv_monitor(message):
|
||
"""仅监控AGV消息,不转发"""
|
||
# 记录到数据库
|
||
VWED.db.execute_update(
|
||
"INSERT INTO agv_logs (device_id, message, created_at) VALUES (?, ?, ?)",
|
||
[message.device_id, str(message.payload), VWED.get_current_time()]
|
||
)
|
||
|
||
# 不转发
|
||
return {"forward": False}
|
||
|
||
VWED.device.register_and_run(
|
||
device_ids=["AGV001"],
|
||
device_type="vehicle",
|
||
brand_name="huarui",
|
||
command_type="state",
|
||
handler=agv_monitor,
|
||
description="AGV监控(仅记录)"
|
||
)
|
||
```
|
||
|
||
#### 示例4:发送自定义响应
|
||
|
||
```python
|
||
def door_controller(message):
|
||
"""门控制器,接收指令后返回确认"""
|
||
action = message.payload.get("instantActions", [])
|
||
|
||
if action:
|
||
action_type = action[0].get("actionType")
|
||
|
||
# 处理开门/关门指令
|
||
if action_type == "openDoor":
|
||
VWED.log.info(f"门 {message.device_id} 正在开启...")
|
||
result = "success"
|
||
elif action_type == "closeDoor":
|
||
VWED.log.info(f"门 {message.device_id} 正在关闭...")
|
||
result = "success"
|
||
else:
|
||
result = "unknown_action"
|
||
|
||
# 不转发原始消息,发送自定义响应
|
||
return {
|
||
"forward": False,
|
||
"response_topic": f"devices/door/{message.device_id}/response",
|
||
"response": {
|
||
"device_id": message.device_id,
|
||
"action": action_type,
|
||
"result": result,
|
||
"timestamp": VWED.get_current_time()
|
||
}
|
||
}
|
||
|
||
return {"forward": False}
|
||
|
||
VWED.device.register_and_run(
|
||
device_ids=["DOOR001", "DOOR002"],
|
||
device_type="door",
|
||
brand_name="huarui",
|
||
command_type="instantActions",
|
||
handler=door_controller,
|
||
description="门设备控制器"
|
||
)
|
||
```
|
||
|
||
#### 示例5:条件转发
|
||
|
||
```python
|
||
def smart_forwarder(message):
|
||
"""根据条件决定是否转发"""
|
||
battery = message.payload.get("batteryState", {}).get("batteryCharge", 100)
|
||
|
||
# 电量低于20%时不转发,发送告警
|
||
if battery < 20:
|
||
VWED.log.warning(f"设备 {message.device_id} 电量过低: {battery}%")
|
||
|
||
# 发送告警消息到告警topic
|
||
return {
|
||
"forward": False,
|
||
"response_topic": "alerts/battery/low",
|
||
"response": {
|
||
"device_id": message.device_id,
|
||
"battery": battery,
|
||
"alert_level": "critical",
|
||
"timestamp": VWED.get_current_time()
|
||
}
|
||
}
|
||
|
||
# 正常转发
|
||
return {"forward": True}
|
||
|
||
VWED.device.register_and_run(
|
||
device_ids=["AGV001", "AGV002", "AGV003"],
|
||
device_type="vehicle",
|
||
brand_name="huarui",
|
||
command_type="state",
|
||
handler=smart_forwarder,
|
||
description="智能转发器(低电量告警)"
|
||
)
|
||
```
|
||
|
||
### 停止设备处理器
|
||
|
||
```python
|
||
VWED.device.stop_handler(device_id: str)
|
||
```
|
||
|
||
**说明**:停止指定设备处理器的运行
|
||
|
||
**参数**:
|
||
- `device_id`: str - 设备ID
|
||
|
||
**示例**:
|
||
```python
|
||
# 停止设备处理器
|
||
VWED.device.stop_handler("AGV001")
|
||
```
|
||
|
||
### 获取运行中的处理器列表
|
||
|
||
```python
|
||
VWED.device.get_running_handlers() -> Dict[str, Any]
|
||
```
|
||
|
||
**说明**:获取所有正在运行的设备处理器信息
|
||
|
||
**返回值**:包含所有设备处理器状态的字典
|
||
|
||
**示例**:
|
||
```python
|
||
handlers = VWED.device.get_running_handlers()
|
||
VWED.log.info(f"运行中的处理器: {handlers}")
|
||
```
|
||
|
||
### 获取设备处理器状态
|
||
|
||
```python
|
||
VWED.device.get_handler_status(device_id: str) -> Dict[str, Any]
|
||
```
|
||
|
||
**说明**:获取指定设备处理器的运行状态
|
||
|
||
**参数**:
|
||
- `device_id`: str - 设备ID
|
||
|
||
**返回值**:设备处理器状态信息
|
||
|
||
**示例**:
|
||
```python
|
||
status = VWED.device.get_handler_status("AGV001")
|
||
VWED.log.info(f"设备状态: {status}")
|
||
```
|
||
|
||
### 主动发布MQTT消息
|
||
|
||
```python
|
||
await VWED.device.publish_message(topic: str, payload: Any)
|
||
```
|
||
|
||
**说明**:主动向指定 topic 发布 MQTT 消息(异步方法)
|
||
|
||
**参数**:
|
||
- `topic`: str - MQTT topic
|
||
- `payload`: dict - 消息载荷
|
||
|
||
**示例**:
|
||
```python
|
||
# 异步发布
|
||
await VWED.device.publish_message(
|
||
topic="factory/agv/command",
|
||
payload={"action": "move", "x": 100, "y": 200}
|
||
)
|
||
|
||
# 或使用同步方式
|
||
VWED.device.sync_publish_message(
|
||
topic="factory/agv/command",
|
||
payload={"action": "move", "x": 100, "y": 200}
|
||
)
|
||
```
|
||
|
||
### 获取支持的设备类型
|
||
|
||
```python
|
||
VWED.device.get_device_types() -> List[str]
|
||
```
|
||
|
||
**说明**:获取系统支持的所有设备类型
|
||
|
||
**返回值**:设备类型列表
|
||
|
||
**示例**:
|
||
```python
|
||
types = VWED.device.get_device_types()
|
||
VWED.log.info(f"支持的设备类型: {types}")
|
||
# 输出: ['vehicle', 'door', 'caller', 'lift', 'conveyor', 'sensor', 'robot', 'camera', 'scanner', 'custom']
|
||
```
|
||
|
||
### 获取支持的设备品牌
|
||
|
||
```python
|
||
VWED.device.get_device_brands() -> List[str]
|
||
```
|
||
|
||
**说明**:获取系统支持的所有设备品牌
|
||
|
||
**返回值**:设备品牌列表
|
||
|
||
**示例**:
|
||
```python
|
||
brands = VWED.device.get_device_brands()
|
||
VWED.log.info(f"支持的设备品牌: {brands}")
|
||
# 输出: ['huarui', 'seer', 'quicktron', 'geek', 'mushiny', 'flashhold', 'hikrobot', 'standard', 'custom']
|
||
```
|
||
|
||
## 完整应用示例
|
||
|
||
### 场景:AGV车队管理系统
|
||
|
||
```python
|
||
# AGV订单处理器
|
||
def process_agv_order(message):
|
||
"""处理AGV订单"""
|
||
order_data = message.payload
|
||
order_id = order_data.get("orderId")
|
||
|
||
# 记录订单到数据库
|
||
VWED.db.execute_update(
|
||
"INSERT INTO agv_orders (device_id, order_id, data, created_at) VALUES (?, ?, ?, ?)",
|
||
[message.device_id, order_id, str(order_data), VWED.get_current_time()]
|
||
)
|
||
|
||
VWED.log.info(f"AGV {message.device_id} 收到订单 {order_id}")
|
||
|
||
# 透传订单到设备
|
||
return {"forward": True}
|
||
|
||
# AGV状态监控
|
||
def monitor_agv_state(message):
|
||
"""监控AGV状态并记录关键信息"""
|
||
state = message.payload
|
||
battery = state.get("batteryState", {}).get("batteryCharge", 100)
|
||
position = state.get("agvPosition", {})
|
||
errors = state.get("errors", [])
|
||
|
||
# 更新设备状态到数据库
|
||
VWED.db.execute_update(
|
||
"UPDATE agv_devices SET battery=?, position_x=?, position_y=?, last_update=? WHERE device_id=?",
|
||
[battery, position.get("x"), position.get("y"), VWED.get_current_time(), message.device_id]
|
||
)
|
||
|
||
# 处理错误
|
||
if errors:
|
||
for error in errors:
|
||
VWED.log.error(f"AGV {message.device_id} 错误: {error.get('errorDescription')}")
|
||
# 发送告警
|
||
VWED.device.sync_publish_message(
|
||
topic=f"alerts/agv/{message.device_id}/error",
|
||
payload={
|
||
"device_id": message.device_id,
|
||
"error": error,
|
||
"timestamp": VWED.get_current_time()
|
||
}
|
||
)
|
||
|
||
# 低电量告警
|
||
if battery < 20:
|
||
VWED.log.warning(f"AGV {message.device_id} 电量低: {battery}%")
|
||
VWED.device.sync_publish_message(
|
||
topic=f"alerts/agv/{message.device_id}/battery",
|
||
payload={
|
||
"device_id": message.device_id,
|
||
"battery": battery,
|
||
"level": "warning",
|
||
"timestamp": VWED.get_current_time()
|
||
}
|
||
)
|
||
|
||
# 转发状态
|
||
return {"forward": True}
|
||
|
||
# 注册华瑞AGV处理器
|
||
VWED.device.register_and_run(
|
||
device_ids=["HR_AGV001", "HR_AGV002", "HR_AGV003"],
|
||
device_type="vehicle",
|
||
brand_name="huarui",
|
||
command_type="order",
|
||
handler=process_agv_order,
|
||
description="华瑞AGV订单处理器"
|
||
)
|
||
|
||
VWED.device.register_and_run(
|
||
device_ids=["HR_AGV001", "HR_AGV002", "HR_AGV003"],
|
||
device_type="vehicle",
|
||
brand_name="huarui",
|
||
command_type="state",
|
||
handler=monitor_agv_state,
|
||
description="华瑞AGV状态监控"
|
||
)
|
||
|
||
# 注册仙工AGV处理器
|
||
VWED.device.register_and_run(
|
||
device_ids=["SEER_AGV001", "SEER_AGV002"],
|
||
device_type="vehicle",
|
||
brand_name="seer",
|
||
command_type="order",
|
||
handler=process_agv_order,
|
||
description="仙工AGV订单处理器"
|
||
)
|
||
|
||
VWED.device.register_and_run(
|
||
device_ids=["SEER_AGV001", "SEER_AGV002"],
|
||
device_type="vehicle",
|
||
brand_name="seer",
|
||
command_type="state",
|
||
handler=monitor_agv_state,
|
||
description="仙工AGV状态监控"
|
||
)
|
||
|
||
VWED.log.info("AGV车队管理系统已启动")
|
||
```
|
||
|
||
## 常见问题
|
||
|
||
### 1. 如何处理多个品牌的设备?
|
||
|
||
分别为每个品牌注册处理器,系统会自动根据品牌生成正确的 MQTT topic。
|
||
|
||
```python
|
||
# 华瑞设备
|
||
VWED.device.register_and_run(
|
||
device_ids=["HR_001", "HR_002"],
|
||
brand_name="huarui",
|
||
command_type="order",
|
||
handler=handler_func
|
||
)
|
||
|
||
# 仙工设备
|
||
VWED.device.register_and_run(
|
||
device_ids=["SEER_001", "SEER_002"],
|
||
brand_name="seer",
|
||
command_type="order",
|
||
handler=handler_func
|
||
)
|
||
```
|
||
|
||
### 2. 一个设备可以注册多个处理器吗?
|
||
|
||
可以,但需要处理不同的指令类型:
|
||
|
||
```python
|
||
# 处理order指令
|
||
VWED.device.register_and_run(
|
||
device_ids=["AGV001"],
|
||
command_type="order",
|
||
handler=order_handler
|
||
)
|
||
|
||
# 处理state指令
|
||
VWED.device.register_and_run(
|
||
device_ids=["AGV001"],
|
||
command_type="state",
|
||
handler=state_handler
|
||
)
|
||
```
|
||
|
||
### 3. 返回值必须是字典吗?
|
||
|
||
是的,必须返回字典类型,否则会记录错误日志并忽略该返回值。
|
||
|
||
### 4. 如何调试消息处理?
|
||
|
||
使用日志记录消息内容:
|
||
|
||
```python
|
||
def debug_handler(message):
|
||
VWED.log.info(f"收到消息:")
|
||
VWED.log.info(f" 设备ID: {message.device_id}")
|
||
VWED.log.info(f" Topic: {message.topic}")
|
||
VWED.log.info(f" 载荷: {message.payload}")
|
||
return {"forward": True}
|
||
```
|
||
|
||
## 注意事项
|
||
|
||
1. **处理函数必须返回字典**:返回值格式必须符合约定
|
||
2. **payload 必须是字典类型**:转发和响应的 payload 都必须是字典
|
||
3. **批量注册时所有设备使用相同的处理函数**:如果需要不同的处理逻辑,分别注册
|
||
4. **处理函数应该快速返回**:避免长时间阻塞,影响其他消息处理
|
||
5. **异常处理**:系统会自动捕获异常并记录日志,但建议在处理函数中添加适当的异常处理
|
||
6. **资源清理**:脚本停止时会自动清理所有注册的设备处理器
|