1426 lines
44 KiB
Markdown
1426 lines
44 KiB
Markdown
# VWED设备处理器使用示例文档
|
||
|
||
## 概述
|
||
|
||
本文档详细说明了在VWED在线脚本中如何使用更新后的设备处理器功能,特别是对VDA5050协议的支持。
|
||
|
||
**核心特点:**
|
||
- **消息透传模式**: 消息直接透传,不进行编解码处理,保持原始数据格式
|
||
- **内置MQTT服务**: 集成自己的MQTT客户端,自主管理连接和重连
|
||
- **多设备类型支持**: 支持vehicle、door、caller、lift、sensor、robot等设备类型
|
||
- **多品牌支持**: 支持huarui、seer、quicktron、geek等主流设备品牌
|
||
- **注册即运行**: 调用 `VWED.device.register_and_run()` 后立即开始监听MQTT消息
|
||
- **自动处理**: 消息到达时自动调用您定义的处理函数
|
||
- **自动转发**: 处理完成后自动发送到指定topic
|
||
- **持续运行**: 一次注册,持续处理消息直到手动停止
|
||
- **脚本ID自动关联**: `script_id` 参数由系统在热加载时自动传入,无需用户手动提供
|
||
|
||
---
|
||
|
||
## 1. VDA5050协议小车设备示例
|
||
|
||
### 1.1 VDA5050小车注册和处理
|
||
|
||
```python
|
||
#!/usr/bin/env python
|
||
# -*- coding: utf-8 -*-
|
||
|
||
def boot():
|
||
"""VDA5050小车处理脚本启动函数"""
|
||
|
||
# 注册华睿小车处理器(消息透传模式)
|
||
VWED.device.register_and_run(
|
||
device_id="huarui_agv_001",
|
||
device_type="vehicle",
|
||
listen_topics=["vda5050/v2/huarui_agv_001/state", "vda5050/v2/huarui_agv_001/factsheet"],
|
||
forward_topics=["fleet/huarui_agv_001/status"],
|
||
handler=vda5050_vehicle_handler,
|
||
device_brand="huarui",
|
||
protocol_type="vda5050", # 协议类型
|
||
description="华睿小车处理器"
|
||
)
|
||
|
||
def vda5050_vehicle_handler(message):
|
||
"""VDA5050小车消息处理器"""
|
||
|
||
device_id = message.device_id
|
||
payload = message.payload
|
||
|
||
VWED.log.sync_info(f"收到VDA5050小车 {device_id} 消息: {payload}")
|
||
|
||
# 根据消息内容处理(消息透传,保持原始格式)
|
||
# 可以根据实际消息格式调整解析逻辑
|
||
message_type = payload.get('message_type', payload.get('command_type', 'unknown'))
|
||
|
||
if message_type == 'state_response' or 'driving' in payload:
|
||
# 处理状态响应
|
||
driving = payload.get('driving', False)
|
||
paused = payload.get('paused', False)
|
||
operating_mode = payload.get('operating_mode', 'unknown')
|
||
battery_state = payload.get('battery_state', {})
|
||
agv_position = payload.get('agv_position', {})
|
||
|
||
VWED.log.sync_info(f"小车状态 - 运行: {driving}, 暂停: {paused}, 模式: {operating_mode}")
|
||
VWED.log.sync_info(f"电池状态: {battery_state}")
|
||
VWED.log.sync_info(f"位置: {agv_position}")
|
||
|
||
# 检查低电量警告
|
||
battery_charge = battery_state.get('batteryCharge', 100)
|
||
if battery_charge < 20:
|
||
VWED.log.sync_warning(f"小车 {device_id} 电量不足: {battery_charge}%")
|
||
|
||
# 发送低电量警报
|
||
alert = {
|
||
"type": "low_battery_alert",
|
||
"device_id": device_id,
|
||
"battery_level": battery_charge,
|
||
"timestamp": VWED.util.now()
|
||
}
|
||
|
||
return {
|
||
"forward": True,
|
||
"topics": ["fleet/alerts", "fleet/huarui_agv_001/status"],
|
||
"payload": [alert, payload]
|
||
}
|
||
|
||
elif message_type == 'factsheet_response' or 'manufacturer' in payload:
|
||
# 处理设备信息响应
|
||
manufacturer = payload.get('manufacturer', '')
|
||
serial_number = payload.get('serial_number', '')
|
||
type_spec = payload.get('type_specification', {})
|
||
|
||
VWED.log.sync_info(f"设备信息 - 制造商: {manufacturer}, 序列号: {serial_number}")
|
||
|
||
# 存储设备信息到缓存
|
||
VWED.data.set(f"device_info_{device_id}", {
|
||
"manufacturer": manufacturer,
|
||
"serial_number": serial_number,
|
||
"type_specification": type_spec,
|
||
"updated_at": VWED.util.now()
|
||
})
|
||
|
||
# 默认转发消息
|
||
return {
|
||
"forward": True,
|
||
"payload": {
|
||
"device_id": device_id,
|
||
"original_data": payload,
|
||
"processed_at": VWED.util.now()
|
||
}
|
||
}
|
||
|
||
# VDA5050指令发送示例
|
||
def send_vda5050_order_example():
|
||
"""发送VDA5050订单指令示例"""
|
||
|
||
# 构建订单数据
|
||
order_data = {
|
||
"order_id": f"order_{int(VWED.util.timestamp())}",
|
||
"nodes": [
|
||
{
|
||
"nodeId": "pickup_station",
|
||
"sequenceId": 1,
|
||
"nodePosition": {"x": 10.5, "y": 20.3, "theta": 0.0, "mapId": "warehouse_map"},
|
||
"actions": [
|
||
{
|
||
"actionType": "pick",
|
||
"actionId": "pick_001",
|
||
"actionDescription": "Pick up cargo",
|
||
"blockingType": "HARD",
|
||
"actionParameters": [
|
||
{"key": "load_type", "value": "box"},
|
||
{"key": "weight", "value": "5"}
|
||
]
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"nodeId": "delivery_station",
|
||
"sequenceId": 2,
|
||
"nodePosition": {"x": 30.0, "y": 45.0, "theta": 1.57, "mapId": "warehouse_map"},
|
||
"actions": [
|
||
{
|
||
"actionType": "drop",
|
||
"actionId": "drop_001",
|
||
"actionDescription": "Drop cargo",
|
||
"blockingType": "HARD"
|
||
}
|
||
]
|
||
}
|
||
],
|
||
"edges": [
|
||
{
|
||
"edgeId": "edge_pickup_to_delivery",
|
||
"sequenceId": 1,
|
||
"startNodeId": "pickup_station",
|
||
"endNodeId": "delivery_station",
|
||
"maxSpeed": 1.5,
|
||
"actions": []
|
||
}
|
||
]
|
||
}
|
||
|
||
# 发送订单
|
||
VWED.device.send_order("huarui_agv_001", order_data)
|
||
VWED.log.sync_info("订单已发送")
|
||
|
||
def send_emergency_stop():
|
||
"""发送紧急停止指令"""
|
||
|
||
emergency_actions = [
|
||
{
|
||
"actionType": "cancelOrder",
|
||
"actionId": f"emergency_stop_{int(VWED.util.timestamp())}",
|
||
"actionDescription": "Emergency stop - cancel current order",
|
||
"blockingType": "HARD"
|
||
}
|
||
]
|
||
|
||
VWED.device.send_instant_action("huarui_agv_001", emergency_actions)
|
||
VWED.log.sync_warning("紧急停止指令已发送")
|
||
|
||
def request_vehicle_state():
|
||
"""请求小车状态"""
|
||
|
||
VWED.device.request_state("huarui_agv_001")
|
||
VWED.log.sync_info("状态查询请求已发送")
|
||
```
|
||
|
||
### 1.2 门设备即时动作示例
|
||
|
||
```python
|
||
def boot():
|
||
"""门设备VDA5050即时动作处理"""
|
||
|
||
# 注册门设备处理器
|
||
VWED.device.register_and_run(
|
||
device_id="door_001",
|
||
device_type="door",
|
||
listen_topics=["door/door_001/status"],
|
||
forward_topics=["facility/door_001/events"],
|
||
handler=door_instant_action_handler,
|
||
device_brand="standard",
|
||
protocol_type="vda5050",
|
||
description="门设备处理器"
|
||
)
|
||
|
||
def door_instant_action_handler(message):
|
||
"""门设备即时动作处理器"""
|
||
|
||
device_id = message.device_id
|
||
payload = message.payload
|
||
|
||
VWED.log.sync_info(f"收到门设备 {device_id} 消息: {payload}")
|
||
|
||
door_status = payload.get('door_status', 'unknown')
|
||
|
||
if door_status == 'access_requested':
|
||
# 处理门禁访问请求
|
||
card_id = payload.get('card_id')
|
||
|
||
# 验证访问权限
|
||
if is_authorized_card(card_id):
|
||
# 发送开门指令
|
||
open_actions = [
|
||
{
|
||
"actionType": "open",
|
||
"actionId": f"open_{int(VWED.util.timestamp())}",
|
||
"actionDescription": f"Open door for card {card_id}",
|
||
"blockingType": "SOFT",
|
||
"actionParameters": [
|
||
{"key": "duration", "value": "5"}, # 开门5秒
|
||
{"key": "card_id", "value": card_id}
|
||
]
|
||
}
|
||
]
|
||
|
||
VWED.device.send_instant_action("door_001", open_actions, "door/door_001/control")
|
||
VWED.log.sync_info(f"已授权开门: {card_id}")
|
||
|
||
return {
|
||
"forward": True,
|
||
"payload": {
|
||
"event": "access_granted",
|
||
"card_id": card_id,
|
||
"timestamp": VWED.util.now()
|
||
}
|
||
}
|
||
else:
|
||
VWED.log.sync_warning(f"未授权访问被拒绝: {card_id}")
|
||
|
||
return {
|
||
"forward": True,
|
||
"payload": {
|
||
"event": "access_denied",
|
||
"card_id": card_id,
|
||
"reason": "unauthorized",
|
||
"timestamp": VWED.util.now()
|
||
}
|
||
}
|
||
|
||
return {"forward": True, "payload": payload}
|
||
|
||
def is_authorized_card(card_id):
|
||
"""检查卡片是否有权限"""
|
||
authorized_cards = VWED.data.get("authorized_cards", ["CARD001", "CARD002", "CARD003"])
|
||
return card_id in authorized_cards
|
||
```
|
||
|
||
### 1.3 通用设备即时动作示例
|
||
|
||
```python
|
||
def boot():
|
||
"""传感器设备即时动作处理"""
|
||
|
||
# 注册传感器设备
|
||
VWED.device.register_and_run(
|
||
device_id="temp_sensor_001",
|
||
device_type="sensor",
|
||
listen_topics=["sensors/temperature/001/data"],
|
||
forward_topics=["environmental/alerts"],
|
||
handler=sensor_instant_action_handler,
|
||
device_brand="standard",
|
||
protocol_type="vda5050",
|
||
description="温度传感器处理器"
|
||
)
|
||
|
||
def sensor_instant_action_handler(message):
|
||
"""传感器即时动作处理器"""
|
||
|
||
device_id = message.device_id
|
||
payload = message.payload
|
||
temperature = payload.get('temperature', 0)
|
||
|
||
VWED.log.sync_info(f"传感器 {device_id} 温度: {temperature}°C")
|
||
|
||
if temperature > 35:
|
||
# 温度过高,发送冷却指令
|
||
cooling_actions = [
|
||
{
|
||
"actionType": "cooling_control",
|
||
"actionId": f"cool_{int(VWED.util.timestamp())}",
|
||
"actionDescription": "Activate cooling system",
|
||
"blockingType": "SOFT",
|
||
"actionParameters": [
|
||
{"key": "target_temp", "value": "25"},
|
||
{"key": "priority", "value": "high"}
|
||
]
|
||
}
|
||
]
|
||
|
||
VWED.device.send_instant_action("temp_sensor_001", cooling_actions, "hvac/control")
|
||
|
||
return {
|
||
"forward": True,
|
||
"payload": {
|
||
"alert_type": "temperature_high",
|
||
"temperature": temperature,
|
||
"action_taken": "cooling_activated",
|
||
"timestamp": VWED.util.now()
|
||
}
|
||
}
|
||
|
||
return {"forward": False} # 正常温度不转发
|
||
```
|
||
|
||
---
|
||
|
||
## 2. 传统协议小车设备示例
|
||
|
||
### 1.1 基本小车命令处理
|
||
|
||
```python
|
||
#!/usr/bin/env python
|
||
# -*- coding: utf-8 -*-
|
||
|
||
def boot():
|
||
"""脚本启动函数"""
|
||
|
||
# 注册并运行AGV设备处理器
|
||
VWED.device.register_and_run(
|
||
device_id="agv_001",
|
||
device_type="vehicle",
|
||
listen_topics=["factory/agv/command"],
|
||
forward_topics=["factory/agv/execute"],
|
||
handler=agv_command_processor,
|
||
device_brand="standard",
|
||
protocol_type="vda5050",
|
||
description="AGV命令处理器 - 添加安全检查"
|
||
)
|
||
|
||
def agv_command_processor(message):
|
||
"""AGV命令处理逻辑 - 消息到达时自动执行"""
|
||
|
||
# 记录接收到的消息
|
||
VWED.log.sync_info(f"收到AGV命令: {message.payload}")
|
||
|
||
command = message.payload.get("command")
|
||
|
||
if command == "move":
|
||
# 添加安全检查逻辑
|
||
x = message.payload.get("x", 0)
|
||
y = message.payload.get("y", 0)
|
||
speed = message.payload.get("speed", 100)
|
||
|
||
# 限制最大速度
|
||
safe_speed = min(speed, 50)
|
||
|
||
# 检查坐标范围
|
||
if x < 0 or x > 1000 or y < 0 or y > 800:
|
||
VWED.log.sync_warning(f"AGV坐标超出安全范围: x={x}, y={y}")
|
||
return {
|
||
"forward": False, # 不转发
|
||
"response": {
|
||
"status": "error",
|
||
"message": "坐标超出安全范围"
|
||
},
|
||
"response_topic": "factory/agv/response"
|
||
}
|
||
|
||
# 构建安全的移动命令
|
||
safe_command = {
|
||
"command": "move_safe",
|
||
"x": x,
|
||
"y": y,
|
||
"speed": safe_speed,
|
||
"safety_check": True,
|
||
"timestamp": VWED.util.now(),
|
||
"processed_by": "agv_001_processor"
|
||
}
|
||
|
||
VWED.log.sync_info(f"AGV安全命令已生成: 速度限制 {speed} -> {safe_speed}")
|
||
|
||
return {
|
||
"forward": True,
|
||
"payload": safe_command
|
||
}
|
||
|
||
elif command == "stop":
|
||
# 紧急停止 - 直接转发
|
||
emergency_stop = {
|
||
"command": "emergency_stop",
|
||
"reason": message.payload.get("reason", "manual_stop"),
|
||
"timestamp": VWED.util.now()
|
||
}
|
||
|
||
return {
|
||
"forward": True,
|
||
"payload": emergency_stop
|
||
}
|
||
|
||
else:
|
||
# 未知命令 - 记录并拒绝
|
||
VWED.log.sync_warning(f"未知AGV命令: {command}")
|
||
return {
|
||
"forward": False,
|
||
"response": {
|
||
"status": "error",
|
||
"message": f"不支持的命令: {command}"
|
||
},
|
||
"response_topic": "factory/agv/response"
|
||
}
|
||
```
|
||
|
||
**MQTT消息流转示例:**
|
||
```
|
||
输入topic: factory/agv/command
|
||
输入消息: {"command": "move", "x": 100, "y": 200, "speed": 80}
|
||
|
||
处理后输出topic: factory/agv/execute
|
||
输出消息: {"command": "move_safe", "x": 100, "y": 200, "speed": 50, "safety_check": true, "timestamp": "2024-01-01T12:00:00", "processed_by": "agv_001_processor"}
|
||
```
|
||
|
||
### 1.2 多AGV协调处理
|
||
|
||
```python
|
||
def boot():
|
||
"""多AGV统一处理"""
|
||
|
||
# 注册AGV车队控制器
|
||
VWED.device.register_and_run(
|
||
device_id="agv_fleet_controller",
|
||
device_type="vehicle",
|
||
listen_topics=["factory/agv/+/command"], # 监听所有AGV命令
|
||
forward_topics=["factory/agv/coordination"],
|
||
handler=agv_fleet_coordinator,
|
||
device_brand="standard",
|
||
protocol_type="vda5050",
|
||
description="AGV车队协调器"
|
||
)
|
||
|
||
def agv_fleet_coordinator(message):
|
||
"""AGV车队协调逻辑"""
|
||
|
||
# 解析AGV ID
|
||
topic_parts = message.topic.split("/")
|
||
agv_id = topic_parts[2] if len(topic_parts) > 2 else "unknown"
|
||
|
||
command = message.payload.get("command")
|
||
|
||
if command == "move":
|
||
# 检查路径冲突
|
||
x = message.payload.get("x")
|
||
y = message.payload.get("y")
|
||
|
||
# 获取其他AGV位置(从缓存中)
|
||
other_agvs = VWED.data.get("agv_positions", {})
|
||
|
||
# 简单的冲突检测
|
||
conflict_detected = False
|
||
for other_id, other_pos in other_agvs.items():
|
||
if other_id != agv_id:
|
||
distance = ((x - other_pos["x"])**2 + (y - other_pos["y"])**2)**0.5
|
||
if distance < 50: # 50cm安全距离
|
||
conflict_detected = True
|
||
break
|
||
|
||
if conflict_detected:
|
||
# 发送等待指令
|
||
wait_command = {
|
||
"command": "wait",
|
||
"reason": "path_conflict",
|
||
"retry_after": 3, # 3秒后重试
|
||
"timestamp": VWED.util.now()
|
||
}
|
||
|
||
VWED.log.sync_warning(f"AGV {agv_id} 路径冲突,指令等待")
|
||
|
||
return {
|
||
"forward": True,
|
||
"topics": [f"factory/agv/{agv_id}/execute"],
|
||
"payload": wait_command
|
||
}
|
||
else:
|
||
# 更新AGV位置缓存
|
||
other_agvs[agv_id] = {"x": x, "y": y, "timestamp": VWED.util.now()}
|
||
VWED.data.set("agv_positions", other_agvs)
|
||
|
||
# 转发原始命令
|
||
return {
|
||
"forward": True,
|
||
"topics": [f"factory/agv/{agv_id}/execute"],
|
||
"payload": message.payload
|
||
}
|
||
|
||
# 默认转发
|
||
return {
|
||
"forward": True,
|
||
"topics": [f"factory/agv/{agv_id}/execute"],
|
||
"payload": message.payload
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 2. 门禁(Door)设备处理示例
|
||
|
||
### 2.1 基本门禁控制
|
||
|
||
```python
|
||
def boot():
|
||
"""门禁系统处理器"""
|
||
|
||
VWED.device.register_and_run(
|
||
device_id="door_main_entrance",
|
||
device_type="door",
|
||
listen_topics=["building/door/access_request"],
|
||
forward_topics=["building/door/control"],
|
||
handler=door_access_controller,
|
||
device_brand="standard",
|
||
protocol_type="vda5050",
|
||
description="主入口门禁控制器"
|
||
)
|
||
|
||
def door_access_controller(message):
|
||
"""门禁访问控制逻辑"""
|
||
|
||
# 获取访问请求信息
|
||
card_id = message.payload.get("card_id")
|
||
door_id = message.payload.get("door_id", "main_entrance")
|
||
request_time = message.payload.get("timestamp", VWED.util.now())
|
||
|
||
VWED.log.sync_info(f"门禁访问请求: 卡号={card_id}, 门={door_id}")
|
||
|
||
# 权限验证逻辑
|
||
if not card_id:
|
||
return {
|
||
"forward": False,
|
||
"response": {"status": "error", "message": "缺少卡号"},
|
||
"response_topic": "building/door/response"
|
||
}
|
||
|
||
# 检查黑名单
|
||
blacklist = VWED.data.get("door_blacklist", [])
|
||
if card_id in blacklist:
|
||
VWED.log.sync_warning(f"黑名单卡号访问被拒绝: {card_id}")
|
||
|
||
return {
|
||
"forward": False,
|
||
"response": {
|
||
"status": "denied",
|
||
"reason": "blacklist",
|
||
"card_id": card_id,
|
||
"timestamp": request_time
|
||
},
|
||
"response_topic": "building/door/response"
|
||
}
|
||
|
||
# 检查白名单
|
||
whitelist = VWED.data.get("door_whitelist", [
|
||
"CARD001", "CARD002", "CARD003" # 默认白名单
|
||
])
|
||
|
||
if card_id in whitelist:
|
||
# 授权开门
|
||
open_command = {
|
||
"action": "open",
|
||
"door_id": door_id,
|
||
"card_id": card_id,
|
||
"authorized_by": "door_main_entrance",
|
||
"open_duration": 5, # 开门5秒
|
||
"timestamp": request_time
|
||
}
|
||
|
||
# 记录访问日志
|
||
access_log = VWED.data.get("door_access_log", [])
|
||
access_log.append({
|
||
"card_id": card_id,
|
||
"door_id": door_id,
|
||
"status": "granted",
|
||
"timestamp": request_time
|
||
})
|
||
|
||
# 只保留最近100条记录
|
||
if len(access_log) > 100:
|
||
access_log = access_log[-100:]
|
||
VWED.data.set("door_access_log", access_log)
|
||
|
||
VWED.log.sync_info(f"门禁访问已授权: {card_id}")
|
||
|
||
return {
|
||
"forward": True,
|
||
"payload": open_command
|
||
}
|
||
else:
|
||
# 未授权卡号
|
||
VWED.log.sync_warning(f"未授权卡号访问被拒绝: {card_id}")
|
||
|
||
return {
|
||
"forward": False,
|
||
"response": {
|
||
"status": "denied",
|
||
"reason": "unauthorized",
|
||
"card_id": card_id,
|
||
"timestamp": request_time
|
||
},
|
||
"response_topic": "building/door/response"
|
||
}
|
||
```
|
||
|
||
### 2.2 门禁状态监控
|
||
|
||
```python
|
||
def boot():
|
||
"""门禁状态监控"""
|
||
|
||
# 监控门禁状态
|
||
VWED.device.register_and_run(
|
||
device_id="door_status_monitor",
|
||
device_type="door",
|
||
listen_topics=["building/door/+/status"],
|
||
forward_topics=["building/security/alerts"],
|
||
handler=door_status_monitor,
|
||
device_brand="standard",
|
||
protocol_type="vda5050",
|
||
description="门禁状态监控器"
|
||
)
|
||
|
||
def door_status_monitor(message):
|
||
"""门禁状态监控逻辑"""
|
||
|
||
# 解析门ID
|
||
topic_parts = message.topic.split("/")
|
||
door_id = topic_parts[2] if len(topic_parts) > 2 else "unknown"
|
||
|
||
status = message.payload.get("status")
|
||
timestamp = message.payload.get("timestamp", VWED.util.now())
|
||
|
||
# 更新门状态缓存
|
||
door_states = VWED.data.get("door_states", {})
|
||
door_states[door_id] = {
|
||
"status": status,
|
||
"timestamp": timestamp,
|
||
"last_update": VWED.util.now()
|
||
}
|
||
VWED.data.set("door_states", door_states)
|
||
|
||
# 异常状态检测
|
||
if status == "forced_open":
|
||
# 门被强制打开 - 发送安全警报
|
||
alert = {
|
||
"type": "security_alert",
|
||
"level": "high",
|
||
"door_id": door_id,
|
||
"event": "forced_open",
|
||
"message": f"门 {door_id} 被强制打开",
|
||
"timestamp": timestamp,
|
||
"requires_action": True
|
||
}
|
||
|
||
VWED.log.sync_error(f"安全警报: 门 {door_id} 被强制打开")
|
||
|
||
return {
|
||
"forward": True,
|
||
"payload": alert
|
||
}
|
||
|
||
elif status == "open_timeout":
|
||
# 门打开超时 - 发送提醒
|
||
alert = {
|
||
"type": "maintenance_alert",
|
||
"level": "medium",
|
||
"door_id": door_id,
|
||
"event": "open_timeout",
|
||
"message": f"门 {door_id} 打开时间过长",
|
||
"timestamp": timestamp,
|
||
"requires_action": False
|
||
}
|
||
|
||
VWED.log.sync_warning(f"维护提醒: 门 {door_id} 打开时间过长")
|
||
|
||
return {
|
||
"forward": True,
|
||
"payload": alert
|
||
}
|
||
|
||
# 正常状态 - 不转发
|
||
return {"forward": False}
|
||
```
|
||
|
||
---
|
||
|
||
## 3. 呼叫器(Caller)设备处理示例
|
||
|
||
### 3.1 基本呼叫处理
|
||
|
||
```python
|
||
def boot():
|
||
"""呼叫器处理系统"""
|
||
|
||
VWED.device.register_and_run(
|
||
device_id="caller_lobby_01",
|
||
device_type="caller",
|
||
listen_topics=["building/caller/signal"],
|
||
forward_topics=["building/caller/response", "building/notification/broadcast"],
|
||
handler=caller_signal_processor,
|
||
device_brand="standard",
|
||
protocol_type="vda5050",
|
||
description="大厅呼叫器信号处理器"
|
||
)
|
||
|
||
def caller_signal_processor(message):
|
||
"""呼叫器信号处理逻辑"""
|
||
|
||
caller_id = message.payload.get("caller_id")
|
||
signal_type = message.payload.get("signal_type")
|
||
location = message.payload.get("location", "unknown")
|
||
priority = message.payload.get("priority", "normal")
|
||
|
||
VWED.log.sync_info(f"收到呼叫信号: {caller_id} - {signal_type} ({priority})")
|
||
|
||
# 根据信号类型处理
|
||
if signal_type == "emergency":
|
||
# 紧急呼叫 - 立即响应
|
||
emergency_response = {
|
||
"type": "emergency_response",
|
||
"caller_id": caller_id,
|
||
"location": location,
|
||
"status": "received",
|
||
"response_time": "immediate",
|
||
"assigned_staff": "security_team",
|
||
"message": "紧急响应团队已派遣",
|
||
"timestamp": VWED.util.now()
|
||
}
|
||
|
||
# 广播紧急通知
|
||
emergency_broadcast = {
|
||
"type": "emergency_broadcast",
|
||
"level": "critical",
|
||
"message": f"紧急呼叫: {location} ({caller_id})",
|
||
"location": location,
|
||
"caller_id": caller_id,
|
||
"timestamp": VWED.util.now()
|
||
}
|
||
|
||
VWED.log.sync_error(f"紧急呼叫处理: {caller_id} 位置: {location}")
|
||
|
||
return {
|
||
"forward": True,
|
||
"topics": ["building/caller/response", "building/notification/broadcast"],
|
||
"payload": [emergency_response, emergency_broadcast]
|
||
}
|
||
|
||
elif signal_type == "service":
|
||
# 服务请求 - 分配服务人员
|
||
service_response = {
|
||
"type": "service_response",
|
||
"caller_id": caller_id,
|
||
"location": location,
|
||
"status": "queued",
|
||
"estimated_time": "5-10 minutes",
|
||
"queue_position": get_queue_position(location),
|
||
"message": "服务请求已接收,请稍候",
|
||
"timestamp": VWED.util.now()
|
||
}
|
||
|
||
VWED.log.sync_info(f"服务请求处理: {caller_id}")
|
||
|
||
return {
|
||
"forward": True,
|
||
"topics": ["building/caller/response"],
|
||
"payload": service_response
|
||
}
|
||
|
||
elif signal_type == "information":
|
||
# 信息查询 - 提供自动回复
|
||
info_response = {
|
||
"type": "information_response",
|
||
"caller_id": caller_id,
|
||
"location": location,
|
||
"status": "completed",
|
||
"information": get_location_info(location),
|
||
"timestamp": VWED.util.now()
|
||
}
|
||
|
||
return {
|
||
"forward": True,
|
||
"topics": ["building/caller/response"],
|
||
"payload": info_response
|
||
}
|
||
|
||
else:
|
||
# 未知信号类型
|
||
error_response = {
|
||
"type": "error_response",
|
||
"caller_id": caller_id,
|
||
"status": "error",
|
||
"message": f"不支持的信号类型: {signal_type}",
|
||
"timestamp": VWED.util.now()
|
||
}
|
||
|
||
return {
|
||
"forward": True,
|
||
"topics": ["building/caller/response"],
|
||
"payload": error_response
|
||
}
|
||
|
||
def get_queue_position(location):
|
||
"""获取队列位置"""
|
||
queue = VWED.data.get(f"service_queue_{location}", [])
|
||
return len(queue) + 1
|
||
|
||
def get_location_info(location):
|
||
"""获取位置信息"""
|
||
location_data = {
|
||
"lobby": "大厅 - 营业时间: 8:00-18:00, 电话: 123-456-7890",
|
||
"elevator": "电梯区域 - 共6部电梯, 楼层: B1-F20",
|
||
"parking": "停车场 - 地下3层, 收费标准: 5元/小时"
|
||
}
|
||
return location_data.get(location, "暂无相关信息")
|
||
```
|
||
|
||
### 3.2 呼叫器多级响应
|
||
|
||
```python
|
||
def boot():
|
||
"""多级呼叫响应系统"""
|
||
|
||
VWED.device.register_and_run(
|
||
device_id="caller_escalation_manager",
|
||
device_type="caller",
|
||
listen_topics=["building/caller/+/urgent"],
|
||
forward_topics=["building/staff/notification"],
|
||
handler=caller_escalation_handler,
|
||
device_brand="standard",
|
||
protocol_type="vda5050",
|
||
description="呼叫升级管理器"
|
||
)
|
||
|
||
def caller_escalation_handler(message):
|
||
"""呼叫升级处理逻辑"""
|
||
|
||
# 解析呼叫器ID
|
||
topic_parts = message.topic.split("/")
|
||
caller_id = topic_parts[2] if len(topic_parts) > 2 else "unknown"
|
||
|
||
urgency_level = message.payload.get("urgency", "medium")
|
||
repeat_count = message.payload.get("repeat_count", 1)
|
||
last_response_time = message.payload.get("last_response_time")
|
||
|
||
# 升级逻辑
|
||
if repeat_count >= 3:
|
||
# 第3次重复呼叫 - 升级到管理层
|
||
escalation = {
|
||
"type": "management_escalation",
|
||
"caller_id": caller_id,
|
||
"urgency": "high",
|
||
"repeat_count": repeat_count,
|
||
"reason": "multiple_unattended_calls",
|
||
"notify_roles": ["supervisor", "manager"],
|
||
"message": f"呼叫器 {caller_id} 连续{repeat_count}次呼叫未响应",
|
||
"timestamp": VWED.util.now()
|
||
}
|
||
|
||
VWED.log.sync_warning(f"呼叫升级到管理层: {caller_id} ({repeat_count}次)")
|
||
|
||
return {
|
||
"forward": True,
|
||
"payload": escalation
|
||
}
|
||
|
||
elif repeat_count == 2:
|
||
# 第2次重复呼叫 - 通知高级员工
|
||
notification = {
|
||
"type": "senior_staff_notification",
|
||
"caller_id": caller_id,
|
||
"urgency": "medium",
|
||
"repeat_count": repeat_count,
|
||
"notify_roles": ["senior_staff"],
|
||
"message": f"呼叫器 {caller_id} 重复呼叫,请优先处理",
|
||
"timestamp": VWED.util.now()
|
||
}
|
||
|
||
return {
|
||
"forward": True,
|
||
"payload": notification
|
||
}
|
||
|
||
else:
|
||
# 首次呼叫 - 正常通知
|
||
notification = {
|
||
"type": "normal_notification",
|
||
"caller_id": caller_id,
|
||
"urgency": urgency_level,
|
||
"notify_roles": ["staff"],
|
||
"message": f"呼叫器 {caller_id} 请求服务",
|
||
"timestamp": VWED.util.now()
|
||
}
|
||
|
||
return {
|
||
"forward": True,
|
||
"payload": notification
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 4. 综合设备处理示例
|
||
|
||
### 4.1 智能楼宇综合管理
|
||
|
||
```python
|
||
def boot():
|
||
"""智能楼宇综合管理系统"""
|
||
|
||
# 环境监控
|
||
VWED.device.register_and_run(
|
||
device_id="environmental_monitor",
|
||
device_type="sensor",
|
||
listen_topics=["building/sensors/+/data"],
|
||
forward_topics=["building/hvac/control", "building/alerts"],
|
||
handler=environmental_processor,
|
||
description="环境监控处理器"
|
||
)
|
||
|
||
# 电梯调度
|
||
VWED.device.register_and_run(
|
||
device_id="elevator_scheduler",
|
||
device_type="lift",
|
||
listen_topics=["building/elevator/call"],
|
||
forward_topics=["building/elevator/+/command"],
|
||
handler=elevator_scheduler,
|
||
description="电梯调度器"
|
||
)
|
||
|
||
# 安全系统集成
|
||
VWED.device.register_and_run(
|
||
device_id="security_integrator",
|
||
device_type="custom",
|
||
listen_topics=["building/security/+/event"],
|
||
forward_topics=["building/security/response"],
|
||
handler=security_event_processor,
|
||
description="安全系统集成器"
|
||
)
|
||
|
||
def environmental_processor(message):
|
||
"""环境数据处理"""
|
||
|
||
# 解析传感器ID
|
||
topic_parts = message.topic.split("/")
|
||
sensor_id = topic_parts[2] if len(topic_parts) > 2 else "unknown"
|
||
|
||
temperature = message.payload.get("temperature")
|
||
humidity = message.payload.get("humidity")
|
||
air_quality = message.payload.get("air_quality")
|
||
|
||
controls = []
|
||
alerts = []
|
||
|
||
# 温度控制
|
||
if temperature is not None:
|
||
if temperature > 26:
|
||
controls.append({
|
||
"device": "hvac",
|
||
"action": "cool",
|
||
"target_temp": 24,
|
||
"reason": f"温度过高: {temperature}°C"
|
||
})
|
||
elif temperature < 20:
|
||
controls.append({
|
||
"device": "hvac",
|
||
"action": "heat",
|
||
"target_temp": 22,
|
||
"reason": f"温度过低: {temperature}°C"
|
||
})
|
||
|
||
# 湿度控制
|
||
if humidity is not None:
|
||
if humidity > 70:
|
||
controls.append({
|
||
"device": "dehumidifier",
|
||
"action": "on",
|
||
"target_humidity": 60,
|
||
"reason": f"湿度过高: {humidity}%"
|
||
})
|
||
|
||
# 空气质量监控
|
||
if air_quality is not None and air_quality < 50:
|
||
alerts.append({
|
||
"type": "air_quality_alert",
|
||
"level": "medium",
|
||
"sensor_id": sensor_id,
|
||
"value": air_quality,
|
||
"message": "空气质量较差,建议开启新风系统"
|
||
})
|
||
|
||
# 返回控制指令和警报
|
||
responses = []
|
||
if controls:
|
||
responses.append(("building/hvac/control", {"controls": controls, "timestamp": VWED.util.now()}))
|
||
if alerts:
|
||
responses.append(("building/alerts", {"alerts": alerts, "timestamp": VWED.util.now()}))
|
||
|
||
if responses:
|
||
return {
|
||
"forward": True,
|
||
"topics": [topic for topic, _ in responses],
|
||
"payload": [payload for _, payload in responses]
|
||
}
|
||
|
||
return {"forward": False}
|
||
|
||
def elevator_scheduler(message):
|
||
"""电梯调度逻辑"""
|
||
|
||
floor_from = message.payload.get("from_floor")
|
||
floor_to = message.payload.get("to_floor")
|
||
priority = message.payload.get("priority", "normal")
|
||
|
||
# 获取电梯状态
|
||
elevator_status = VWED.data.get("elevator_status", {
|
||
"elevator_1": {"current_floor": 1, "status": "idle"},
|
||
"elevator_2": {"current_floor": 10, "status": "busy"},
|
||
"elevator_3": {"current_floor": 5, "status": "idle"}
|
||
})
|
||
|
||
# 选择最佳电梯
|
||
best_elevator = None
|
||
min_distance = float('inf')
|
||
|
||
for elevator_id, status in elevator_status.items():
|
||
if status["status"] == "idle":
|
||
distance = abs(status["current_floor"] - floor_from)
|
||
if distance < min_distance:
|
||
min_distance = distance
|
||
best_elevator = elevator_id
|
||
|
||
if best_elevator:
|
||
# 分配电梯
|
||
command = {
|
||
"elevator_id": best_elevator,
|
||
"action": "goto",
|
||
"from_floor": floor_from,
|
||
"to_floor": floor_to,
|
||
"priority": priority,
|
||
"estimated_time": min_distance * 3, # 假设每层3秒
|
||
"timestamp": VWED.util.now()
|
||
}
|
||
|
||
# 更新电梯状态
|
||
elevator_status[best_elevator]["status"] = "busy"
|
||
VWED.data.set("elevator_status", elevator_status)
|
||
|
||
return {
|
||
"forward": True,
|
||
"topics": [f"building/elevator/{best_elevator}/command"],
|
||
"payload": command
|
||
}
|
||
else:
|
||
# 无空闲电梯 - 加入队列
|
||
queue = VWED.data.get("elevator_queue", [])
|
||
queue.append({
|
||
"from_floor": floor_from,
|
||
"to_floor": floor_to,
|
||
"priority": priority,
|
||
"request_time": VWED.util.now()
|
||
})
|
||
VWED.data.set("elevator_queue", queue)
|
||
|
||
return {
|
||
"forward": False,
|
||
"response": {
|
||
"status": "queued",
|
||
"position": len(queue),
|
||
"estimated_wait": len(queue) * 30 # 每个请求30秒
|
||
},
|
||
"response_topic": "building/elevator/response"
|
||
}
|
||
|
||
def security_event_processor(message):
|
||
"""安全事件处理"""
|
||
|
||
event_type = message.payload.get("event_type")
|
||
location = message.payload.get("location")
|
||
severity = message.payload.get("severity", "medium")
|
||
|
||
if event_type == "intrusion_detected":
|
||
# 入侵检测 - 立即响应
|
||
response = {
|
||
"action": "lockdown",
|
||
"location": location,
|
||
"alert_level": "high",
|
||
"notify_security": True,
|
||
"timestamp": VWED.util.now()
|
||
}
|
||
|
||
return {
|
||
"forward": True,
|
||
"payload": response
|
||
}
|
||
|
||
elif event_type == "fire_alarm":
|
||
# 火警 - 疏散程序
|
||
response = {
|
||
"action": "evacuation",
|
||
"location": location,
|
||
"alert_level": "critical",
|
||
"notify_fire_dept": True,
|
||
"timestamp": VWED.util.now()
|
||
}
|
||
|
||
return {
|
||
"forward": True,
|
||
"payload": response
|
||
}
|
||
|
||
# 其他事件正常处理
|
||
return {
|
||
"forward": True,
|
||
"payload": message.payload
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 5. 设备处理器管理功能
|
||
|
||
### 5.1 设备状态监控
|
||
|
||
```python
|
||
def boot():
|
||
"""设备管理和监控"""
|
||
|
||
# 注册设备状态监控
|
||
VWED.device.register_and_run(
|
||
device_id="device_status_monitor",
|
||
device_type="custom",
|
||
listen_topics=["system/device/+/heartbeat"],
|
||
forward_topics=["system/alerts"],
|
||
handler=device_heartbeat_monitor,
|
||
description="设备心跳监控器"
|
||
)
|
||
|
||
# 定时检查设备状态
|
||
VWED.timer.interval(
|
||
seconds=60, # 每分钟检查一次
|
||
handler=check_device_status
|
||
)
|
||
|
||
def device_heartbeat_monitor(message):
|
||
"""设备心跳监控"""
|
||
|
||
device_id = message.payload.get("device_id")
|
||
status = message.payload.get("status")
|
||
timestamp = message.payload.get("timestamp", VWED.util.now())
|
||
|
||
# 更新设备心跳记录
|
||
heartbeats = VWED.data.get("device_heartbeats", {})
|
||
heartbeats[device_id] = {
|
||
"status": status,
|
||
"last_heartbeat": timestamp,
|
||
"updated_at": VWED.util.now()
|
||
}
|
||
VWED.data.set("device_heartbeats", heartbeats)
|
||
|
||
# 如果设备状态异常,发送警报
|
||
if status in ["error", "offline", "malfunction"]:
|
||
alert = {
|
||
"type": "device_alert",
|
||
"device_id": device_id,
|
||
"status": status,
|
||
"message": f"设备 {device_id} 状态异常: {status}",
|
||
"timestamp": timestamp,
|
||
"requires_attention": True
|
||
}
|
||
|
||
return {
|
||
"forward": True,
|
||
"payload": alert
|
||
}
|
||
|
||
return {"forward": False}
|
||
|
||
def check_device_status():
|
||
"""定时检查设备状态"""
|
||
|
||
current_time = VWED.util.timestamp()
|
||
heartbeats = VWED.data.get("device_heartbeats", {})
|
||
|
||
for device_id, info in heartbeats.items():
|
||
last_heartbeat = info.get("last_heartbeat")
|
||
|
||
if last_heartbeat:
|
||
# 检查是否超过5分钟没有心跳
|
||
if current_time - last_heartbeat > 300: # 5分钟
|
||
VWED.log.sync_warning(f"设备 {device_id} 心跳超时")
|
||
|
||
# 发送超时警报
|
||
VWED.device.sync_publish_message(
|
||
topic="system/alerts",
|
||
payload={
|
||
"type": "device_timeout",
|
||
"device_id": device_id,
|
||
"last_seen": last_heartbeat,
|
||
"timeout_duration": current_time - last_heartbeat,
|
||
"timestamp": VWED.util.now()
|
||
}
|
||
)
|
||
```
|
||
|
||
### 5.2 设备处理器动态管理
|
||
|
||
```python
|
||
def boot():
|
||
"""动态设备管理"""
|
||
|
||
# 设备管理控制器
|
||
VWED.device.register_and_run(
|
||
device_id="device_manager",
|
||
device_type="custom",
|
||
listen_topics=["system/device/control"],
|
||
forward_topics=["system/device/response"],
|
||
handler=device_management_handler,
|
||
description="设备动态管理控制器"
|
||
)
|
||
|
||
def device_management_handler(message):
|
||
"""设备管理控制处理"""
|
||
|
||
action = message.payload.get("action")
|
||
device_id = message.payload.get("device_id")
|
||
|
||
if action == "status":
|
||
# 获取设备状态
|
||
status = VWED.device.get_handler_status(device_id)
|
||
|
||
return {
|
||
"forward": True,
|
||
"payload": {
|
||
"action": "status_response",
|
||
"device_id": device_id,
|
||
"status": status,
|
||
"timestamp": VWED.util.now()
|
||
}
|
||
}
|
||
|
||
elif action == "stop":
|
||
# 停止设备处理器
|
||
try:
|
||
VWED.device.stop_handler(device_id)
|
||
|
||
return {
|
||
"forward": True,
|
||
"payload": {
|
||
"action": "stop_response",
|
||
"device_id": device_id,
|
||
"status": "stopped",
|
||
"timestamp": VWED.util.now()
|
||
}
|
||
}
|
||
except Exception as e:
|
||
return {
|
||
"forward": True,
|
||
"payload": {
|
||
"action": "stop_response",
|
||
"device_id": device_id,
|
||
"status": "error",
|
||
"error": str(e),
|
||
"timestamp": VWED.util.now()
|
||
}
|
||
}
|
||
|
||
elif action == "restart":
|
||
# 重启设备处理器
|
||
try:
|
||
VWED.device.restart_handler(device_id)
|
||
|
||
return {
|
||
"forward": True,
|
||
"payload": {
|
||
"action": "restart_response",
|
||
"device_id": device_id,
|
||
"status": "restarted",
|
||
"timestamp": VWED.util.now()
|
||
}
|
||
}
|
||
except Exception as e:
|
||
return {
|
||
"forward": True,
|
||
"payload": {
|
||
"action": "restart_response",
|
||
"device_id": device_id,
|
||
"status": "error",
|
||
"error": str(e),
|
||
"timestamp": VWED.util.now()
|
||
}
|
||
}
|
||
|
||
elif action == "list":
|
||
# 获取所有设备列表
|
||
handlers = VWED.device.get_running_handlers()
|
||
|
||
return {
|
||
"forward": True,
|
||
"payload": {
|
||
"action": "list_response",
|
||
"handlers": handlers,
|
||
"timestamp": VWED.util.now()
|
||
}
|
||
}
|
||
|
||
else:
|
||
return {
|
||
"forward": True,
|
||
"payload": {
|
||
"action": "error_response",
|
||
"message": f"不支持的操作: {action}",
|
||
"timestamp": VWED.util.now()
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 6. 调试和故障排除
|
||
|
||
### 6.1 设备处理器调试
|
||
|
||
```python
|
||
def boot():
|
||
"""设备处理器调试模式"""
|
||
|
||
# 启用调试模式
|
||
debug_mode = True
|
||
|
||
if debug_mode:
|
||
# 注册调试设备处理器
|
||
VWED.device.register_and_run(
|
||
device_id="debug_processor",
|
||
device_type="custom",
|
||
listen_topics=["debug/+/+"], # 监听所有调试消息
|
||
forward_topics=[],
|
||
handler=debug_message_handler,
|
||
description="调试消息处理器"
|
||
)
|
||
|
||
def debug_message_handler(message):
|
||
"""调试消息处理"""
|
||
|
||
# 详细记录消息信息
|
||
VWED.log.sync_info(f"调试消息 - Topic: {message.topic}")
|
||
VWED.log.sync_info(f"调试消息 - Payload: {message.payload}")
|
||
VWED.log.sync_info(f"调试消息 - Device: {message.device_id}")
|
||
VWED.log.sync_info(f"调试消息 - Type: {message.device_type}")
|
||
VWED.log.sync_info(f"调试消息 - Timestamp: {message.timestamp}")
|
||
|
||
# 保存调试日志
|
||
debug_logs = VWED.data.get("debug_logs", [])
|
||
debug_logs.append({
|
||
"topic": message.topic,
|
||
"payload": message.payload,
|
||
"device_id": message.device_id,
|
||
"device_type": message.device_type.value if hasattr(message.device_type, 'value') else str(message.device_type),
|
||
"timestamp": message.timestamp,
|
||
"processed_at": VWED.util.now()
|
||
})
|
||
|
||
# 只保留最近50条调试日志
|
||
if len(debug_logs) > 50:
|
||
debug_logs = debug_logs[-50:]
|
||
VWED.data.set("debug_logs", debug_logs)
|
||
|
||
# 不转发调试消息
|
||
return {"forward": False}
|
||
|
||
def get_debug_statistics():
|
||
"""获取调试统计信息"""
|
||
|
||
handlers = VWED.device.get_running_handlers()
|
||
debug_logs = VWED.data.get("debug_logs", [])
|
||
|
||
return {
|
||
"total_handlers": handlers.get("device_count", 0),
|
||
"running_handlers": handlers.get("running_devices", 0),
|
||
"debug_log_count": len(debug_logs),
|
||
"last_debug_time": debug_logs[-1]["processed_at"] if debug_logs else None
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 总结
|
||
|
||
本文档提供了VWED设备处理器的完整使用示例,包括:
|
||
|
||
### 主要功能
|
||
1. **设备类型支持** - 小车、门禁、呼叫器、电梯、传感器等
|
||
2. **注册即运行** - 一次注册,持续监听和处理MQTT消息
|
||
3. **自动转发** - 处理完成后自动发送到指定topic
|
||
4. **动态管理** - 支持运行时启停、重启设备处理器
|
||
5. **状态监控** - 实时监控设备状态和处理统计
|
||
|
||
### 关键特点
|
||
- **分离式设计** - 注册和处理逻辑分离,保持代码清晰
|
||
- **消息驱动** - MQTT消息自动触发处理,无需手动调用
|
||
- **错误处理** - 完善的异常处理和重试机制
|
||
- **扩展性强** - 支持自定义设备类型和处理逻辑
|
||
- **易于调试** - 详细的日志记录和调试功能
|
||
|
||
### 使用建议
|
||
1. 根据实际设备类型选择合适的device_type
|
||
2. 合理设置listen_topics和forward_topics
|
||
3. 在处理函数中添加充分的错误处理
|
||
4. 使用VWED.log记录关键操作日志
|
||
5. 利用VWED.data缓存必要的状态信息
|
||
|
||
通过这些示例,您可以快速掌握VWED设备处理器的使用方法,实现复杂的设备逻辑处理和MQTT消息转发。 |