VWED_server/docs/device_quick_start_example.py

730 lines
22 KiB
Python
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.

#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
设备处理器快速入门示例
演示新的简化批量注册接口 - 自动生成MQTT topics
支持华瑞、仙工等品牌小车的批量注册和自动消息转发
基于device_ids的通用设备注册方法
"""
def boot():
"""脚本启动函数 - 演示新的简化接口"""
print("=== 设备处理器快速入门示例 ===")
# 1. 最简单的自动透传(推荐用法)
example_1_auto_forward()
# 2. 自定义处理器
example_2_custom_handler()
# 3. 指定特定指令类型
example_3_specific_commands()
# 4. 仙工品牌示例
example_4_seer_vehicles()
# 5. 混合设备类型示例
example_5_mixed_device_types()
print("=== 设备处理器注册完成 ===")
def example_1_auto_forward():
"""示例1: 最简单的自动透传(推荐用法)"""
print("1. 批量注册华瑞小车自动透传")
# 批量注册华瑞小车系统自动生成topics和透传逻辑
VWED.device.register_and_run(
device_ids=["AGV001", "AGV002", "AGV003"],
device_type="vehicle",
brand_name="huarui",
script_id="example_script_001",
description="华瑞小车批量自动透传"
)
print("华瑞小车批量注册成功")
# 系统会自动:
# 监听: oagv/v2/VWED_001_IRAYPLE/+/order 等
# 转发: uagv/v2.0.0/IRAYPLE/AGV001/order 等
def example_2_custom_handler():
"""示例2: 自定义处理器"""
print("2. 自定义华瑞小车处理器")
def my_custom_handler(message):
"""自定义消息处理器"""
print(f"收到消息: {message.topic}")
print(f"载荷: {message.payload}")
# 可以修改消息内容
modified_payload = message.payload.copy()
modified_payload["processed_by"] = "my_script"
modified_payload["processed_at"] = VWED.util.now()
# 返回处理结果
return {
"forward": True, # 是否转发
"payload": modified_payload, # 修改后的载荷
"topics": message.target_topics # 转发到的topics
}
# 使用自定义处理器批量注册
VWED.device.register_and_run(
device_ids=["AGV001", "AGV002"],
device_type="vehicle",
brand_name="huarui",
handler=my_custom_handler,
script_id="example_script_002",
description="自定义华瑞小车处理器"
)
def example_3_specific_commands():
"""示例3: 指定特定指令类型"""
print("3. 指定特定指令类型")
def order_and_state_handler(message):
"""只处理order和state指令"""
command_type = message.topic.split('/')[-1]
if command_type == "order":
print(f"处理订单指令: {message.payload}")
elif command_type == "state":
print(f"处理状态消息: {message.payload}")
return {
"forward": True,
"payload": message.payload
}
# 只监听和转发order和state指令
VWED.device.register_and_run(
device_ids=["AGV001"],
device_type="vehicle",
brand_name="seer",
command_types=["order", "state"], # 只处理这两种指令
handler=order_and_state_handler,
)
def example_4_seer_vehicles():
"""示例4: 仙工品牌小车处理"""
print("4. 仙工品牌小车处理")
def seer_handler(message):
"""仙工小车处理器"""
print(f"仙工小车消息: {message.topic}")
# 根据消息类型进行不同处理
command_type = message.topic.split('/')[-1]
if command_type == "state":
# 状态消息特殊处理
battery_level = message.payload.get("batteryLevel", 100)
if battery_level < 20:
print(f"警告:小车电量低 {battery_level}%")
return {"forward": True, "payload": message.payload}
# 批量注册仙工小车处理器
VWED.device.register_and_run(
device_ids=["SEER_001", "SEER_002"],
device_type="vehicle",
brand_name="seer",
handler=seer_handler,
script_id="example_script_004",
description="仙工小车处理器"
)
def example_5_mixed_device_types():
"""示例5: 混合设备类型批量注册"""
print("5. 混合设备类型批量注册")
def mixed_handler(message):
"""混合设备处理器"""
print(f"混合设备处理器收到消息: {message.topic}")
device_id = message.topic.split('/')[-2] # 提取设备ID
print(f"设备 {device_id} 发送消息")
return {"forward": True, "payload": message.payload}
# 批量注册多个设备(包括小车和其他设备类型)
VWED.device.register_and_run(
device_ids=["AGV_001", "DOOR_001", "CALLER_001"],
device_type="vehicle", # 主要设备类型
brand_name="huarui",
handler=mixed_handler,
script_id="example_script_005",
description="混合设备批量处理器"
)
def register_door_handler():
"""注册门禁处理器"""
VWED.device.register_and_run(
device_ids=["door_demo_main"],
device_type="door",
handler=door_access_controller,
script_id="door_demo_001",
description="演示门禁访问控制器"
)
def register_caller_handler():
"""注册呼叫器处理器"""
VWED.device.register_and_run(
device_ids=["caller_demo_lobby"],
device_type="caller",
handler=caller_signal_processor,
script_id="caller_demo_001",
description="演示呼叫器信号处理器"
)
def register_sensor_handler():
"""注册传感器处理器"""
VWED.device.register_and_run(
device_ids=["sensor_demo_env"],
device_type="sensor",
handler=environmental_processor,
script_id="sensor_demo_001",
description="演示环境传感器处理器"
)
# ==================== 设备处理函数 ====================
def agv_command_processor(message):
"""AGV命令处理函数 - MQTT消息到达时自动执行"""
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)
# 限制最大速度为50
safe_speed = min(speed, 50)
# 构建安全移动命令
safe_command = {
"command": "move_safe",
"x": x,
"y": y,
"speed": safe_speed,
"safety_check": True,
"original_speed": speed,
"processed_by": "agv_demo_001",
"timestamp": VWED.util.now()
}
if speed != safe_speed:
VWED.log.sync_warning(f"AGV速度已限制: {speed} -> {safe_speed}")
VWED.log.sync_info(f"AGV安全命令生成: {safe_command}")
return {
"forward": True,
"payload": safe_command
}
elif command == "stop":
# 停止命令 - 直接转发
stop_command = {
"command": "emergency_stop",
"reason": message.payload.get("reason", "manual"),
"timestamp": VWED.util.now()
}
VWED.log.sync_info("AGV紧急停止命令")
return {
"forward": True,
"payload": stop_command
}
else:
# 未知命令
VWED.log.sync_warning(f"AGV未知命令: {command}")
return {
"forward": False,
"response": {
"status": "error",
"message": f"不支持的命令: {command}"
},
"response_topic": "demo/agv/response"
}
def huarui_agv_processor(message):
"""华睿AGV处理函数 - 支持华睿协议自动编码/解码"""
VWED.log.sync_info(f"华睿AGV收到消息: {message.payload}")
# 由于启用了auto_encode=True华睿协议已自动解码message.payload
# 这里收到的是标准格式的数据
action = message.payload.get("action")
if action == "status_update":
# 状态更新 - 添加业务逻辑处理
status = message.payload.get("status")
position = message.payload.get("position", {})
battery = message.payload.get("battery", 0)
VWED.log.sync_info(f"华睿AGV状态: {status}, 位置: {position}, 电量: {battery}%")
# 低电量预警
if battery < 20:
VWED.log.sync_warning(f"华睿AGV电量不足: {battery}%")
# 发送充电指令(会自动编码为华睿协议格式)
return {
"forward": True,
"payload": {
"action": "move",
"x": 0, # 充电桩位置
"y": 0,
"speed": 30,
"task_id": f"charge_{VWED.util.timestamp()}"
}
}
# 正常状态,无需特殊处理
return {"forward": False}
else:
VWED.log.sync_info(f"华睿AGV消息已处理: {action}")
return {"forward": False}
def seer_agv_processor(message):
"""仙工AGV处理函数 - 支持仙工协议自动编码/解码"""
VWED.log.sync_info(f"仙工AGV收到消息: {message.payload}")
# 仙工协议已自动解码,这里处理标准格式数据
action = message.payload.get("action")
if action == "status_update":
status = message.payload.get("status")
position = message.payload.get("position", {})
velocity = message.payload.get("velocity", 0)
VWED.log.sync_info(f"仙工AGV状态: {status}, 位置: {position}, 速度: {velocity}")
# 仙工特定的逻辑处理
if status == "error":
VWED.log.sync_error("仙工AGV出现错误发送重启指令")
# 发送重启指令(会自动编码为仙工协议格式)
return {
"forward": True,
"payload": {
"action": "stop",
"reason": "error_recovery"
}
}
return {"forward": False}
else:
VWED.log.sync_info(f"仙工AGV消息已处理: {action}")
return {"forward": False}
def door_access_controller(message):
"""门禁访问控制函数"""
card_id = message.payload.get("card_id")
door_id = message.payload.get("door_id", "main")
VWED.log.sync_info(f"门禁访问请求: 卡号={card_id}, 门={door_id}")
# 简单的白名单验证
whitelist = ["DEMO001", "DEMO002", "DEMO003"]
if card_id in whitelist:
# 授权开门
open_command = {
"action": "open",
"door_id": door_id,
"card_id": card_id,
"authorized": True,
"open_duration": 5, # 5秒
"timestamp": VWED.util.now()
}
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",
"card_id": card_id,
"reason": "unauthorized",
"timestamp": VWED.util.now()
},
"response_topic": "demo/door/response"
}
def caller_signal_processor(message):
"""呼叫器信号处理函数"""
caller_id = message.payload.get("caller_id")
signal_type = message.payload.get("signal_type", "service")
location = message.payload.get("location", "unknown")
VWED.log.sync_info(f"呼叫信号: {caller_id} - {signal_type} @ {location}")
if signal_type == "emergency":
# 紧急呼叫
response = {
"type": "emergency_response",
"caller_id": caller_id,
"location": location,
"status": "received",
"response_team": "security",
"estimated_time": "2 minutes",
"message": "紧急响应团队已派遣",
"timestamp": VWED.util.now()
}
VWED.log.sync_error(f"紧急呼叫处理: {caller_id}")
elif signal_type == "service":
# 服务请求
response = {
"type": "service_response",
"caller_id": caller_id,
"location": location,
"status": "queued",
"queue_position": 1,
"estimated_time": "5 minutes",
"message": "服务请求已接收",
"timestamp": VWED.util.now()
}
VWED.log.sync_info(f"服务请求处理: {caller_id}")
else:
# 其他类型
response = {
"type": "info_response",
"caller_id": caller_id,
"status": "received",
"message": "请求已收到",
"timestamp": VWED.util.now()
}
return {
"forward": True,
"payload": response
}
def environmental_processor(message):
"""环境传感器处理函数"""
sensor_id = message.payload.get("sensor_id")
temperature = message.payload.get("temperature")
humidity = message.payload.get("humidity")
VWED.log.sync_info(f"环境数据: 传感器={sensor_id}, 温度={temperature}°C, 湿度={humidity}%")
control_commands = []
# 温度控制逻辑
if temperature is not None:
if temperature > 26:
control_commands.append({
"device": "ac",
"action": "cool",
"target_temp": 24,
"reason": f"温度过高: {temperature}°C"
})
VWED.log.sync_info(f"启动制冷: 目标温度24°C")
elif temperature < 20:
control_commands.append({
"device": "heater",
"action": "heat",
"target_temp": 22,
"reason": f"温度过低: {temperature}°C"
})
VWED.log.sync_info(f"启动加热: 目标温度22°C")
# 湿度控制逻辑
if humidity is not None and humidity > 70:
control_commands.append({
"device": "dehumidifier",
"action": "on",
"target_humidity": 60,
"reason": f"湿度过高: {humidity}%"
})
VWED.log.sync_info(f"启动除湿: 目标湿度60%")
if control_commands:
# 有控制指令需要发送
hvac_control = {
"sensor_id": sensor_id,
"controls": control_commands,
"timestamp": VWED.util.now()
}
return {
"forward": True,
"payload": hvac_control
}
else:
# 环境正常,不需要控制
VWED.log.sync_info("环境参数正常,无需调整")
return {"forward": False}
# ==================== 测试和管理函数 ====================
def test_device_handlers():
"""测试设备处理器功能"""
VWED.log.sync_info("开始测试设备处理器...")
# 测试AGV
VWED.device.sync_publish_message(
topic="demo/agv/command",
payload={
"command": "move",
"x": 100,
"y": 200,
"speed": 80
}
)
# 测试门禁
VWED.device.sync_publish_message(
topic="demo/door/access_request",
payload={
"card_id": "DEMO001",
"door_id": "main_entrance"
}
)
# 测试呼叫器
VWED.device.sync_publish_message(
topic="demo/caller/signal",
payload={
"caller_id": "LOBBY_01",
"signal_type": "service",
"location": "main_lobby"
}
)
# 测试传感器
VWED.device.sync_publish_message(
topic="demo/sensor/data",
payload={
"sensor_id": "ENV_001",
"temperature": 28,
"humidity": 75
}
)
VWED.log.sync_info("设备处理器测试完成")
def get_device_status():
"""获取所有设备处理器状态"""
handlers = VWED.device.get_running_handlers()
VWED.log.sync_info("=== 设备处理器状态 ===")
VWED.log.sync_info(f"设备总数: {handlers.get('device_count', 0)}")
VWED.log.sync_info(f"运行中: {handlers.get('running_devices', 0)}")
devices = handlers.get("devices", {})
for device_id, status in devices.items():
VWED.log.sync_info(f"设备 {device_id}: 消息处理 {status.get('total_messages', 0)}")
return handlers
# ==================== 工具函数 ====================
def stop_all_handlers():
"""停止所有设备处理器(用于测试)"""
handlers = ["agv_demo_001", "door_demo_main", "caller_demo_lobby", "sensor_demo_env"]
for device_id in handlers:
try:
VWED.device.stop_handler(device_id)
VWED.log.sync_info(f"已停止设备处理器: {device_id}")
except Exception as e:
VWED.log.sync_warning(f"停止设备处理器失败 {device_id}: {e}")
def show_device_types():
"""显示支持的设备类型"""
device_types = VWED.device.get_device_types()
VWED.log.sync_info(f"支持的设备类型: {', '.join(device_types)}")
return device_types
def show_device_brands():
"""显示支持的设备品牌"""
device_brands = VWED.device.get_device_brands()
VWED.log.sync_info(f"支持的设备品牌: {', '.join(device_brands)}")
return device_brands
def show_available_protocols():
"""显示所有可用的协议"""
protocols = VWED.device.get_protocols()
VWED.log.sync_info("=== 可用的设备协议 ===")
for protocol_key, protocol_info in protocols.items():
brand = protocol_info.get("brand", "unknown")
device_type = protocol_info.get("device_type", "unknown")
commands = protocol_info.get("supported_commands", [])
VWED.log.sync_info(f"协议: {protocol_key}")
VWED.log.sync_info(f" 品牌: {brand}")
VWED.log.sync_info(f" 设备类型: {device_type}")
VWED.log.sync_info(f" 支持指令: {', '.join(commands)}")
VWED.log.sync_info("")
return protocols
def test_protocol_encoding():
"""测试协议编码功能"""
VWED.log.sync_info("=== 测试协议编码功能 ===")
# 测试华睿协议编码
huarui_test = VWED.device.test_protocol_encoding(
protocol_key="huarui_vehicle",
test_command={
"action": "move",
"x": 100,
"y": 200,
"speed": 50,
"task_id": "test_001"
}
)
if huarui_test["success"]:
VWED.log.sync_info("华睿协议编码测试成功:")
VWED.log.sync_info(f" 原始指令: {huarui_test['original']}")
VWED.log.sync_info(f" 编码后: {huarui_test['encoded']}")
else:
VWED.log.sync_error(f"华睿协议编码测试失败: {huarui_test['error']}")
# 测试仙工协议编码
seer_test = VWED.device.test_protocol_encoding(
protocol_key="seer_vehicle",
test_command={
"action": "move",
"x": 150,
"y": 250,
"speed": 1500, # 仙工使用mm/s
"task_id": "test_002"
}
)
if seer_test["success"]:
VWED.log.sync_info("仙工协议编码测试成功:")
VWED.log.sync_info(f" 原始指令: {seer_test['original']}")
VWED.log.sync_info(f" 编码后: {seer_test['encoded']}")
else:
VWED.log.sync_error(f"仙工协议编码测试失败: {seer_test['error']}")
return {"huarui": huarui_test, "seer": seer_test}
def register_custom_protocol_example():
"""注册自定义协议示例"""
def my_encode(command):
"""自定义编码函数"""
return {
"header": {
"version": "1.0",
"timestamp": VWED.util.timestamp()
},
"body": {
"cmd_type": command.get("action", "unknown"),
"params": command
}
}
def my_decode(response):
"""自定义解码函数"""
body = response.get("body", {})
return {
"action": "status_update",
"data": body,
"decoded_at": VWED.util.now()
}
# 注册自定义协议
VWED.device.register_custom_protocol(
protocol_key="my_custom_agv",
brand="my_brand",
device_type="vehicle",
encode_func=my_encode,
decode_func=my_decode,
supported_commands=["move", "stop", "pause", "status"]
)
VWED.log.sync_info("自定义协议注册完成")
# 测试自定义协议
test_result = VWED.device.test_protocol_encoding(
protocol_key="my_custom_agv",
test_command={
"action": "move",
"x": 300,
"y": 400
}
)
if test_result["success"]:
VWED.log.sync_info("自定义协议测试成功:")
VWED.log.sync_info(f" 编码结果: {test_result['encoded']}")
else:
VWED.log.sync_error(f"自定义协议测试失败: {test_result['error']}")
return test_result