165 lines
7.5 KiB
Python
165 lines
7.5 KiB
Python
|
|
# -*- coding: utf-8 -*-
|
|||
|
|
"""
|
|||
|
|
通信中间件框架 - 完整使用示例
|
|||
|
|
演示:工厂创建、发布、订阅、对比三种协议
|
|||
|
|
"""
|
|||
|
|
|
|||
|
|
import asyncio
|
|||
|
|
import logging
|
|||
|
|
|
|||
|
|
from adapters.dds_adapter import DDSAdapterComm
|
|||
|
|
from adapters.kafka_adapter import KafkaAdapterComm
|
|||
|
|
# ── 注册所有内置适配器 ─────────────────────────────────────────────
|
|||
|
|
from adapters.redis_adapter import RedisAdapterComm
|
|||
|
|
from uas.comm.factory import CommAdapterFactory
|
|||
|
|
from uas.comm.message import Message, MessagePriority
|
|||
|
|
|
|||
|
|
CommAdapterFactory.register("redis", RedisAdapterComm)
|
|||
|
|
CommAdapterFactory.register("kafka", KafkaAdapterComm)
|
|||
|
|
CommAdapterFactory.register("dds", DDSAdapterComm)
|
|||
|
|
|
|||
|
|
# ── 日志配置 ──────────────────────────────────────────────────────
|
|||
|
|
logging.basicConfig(
|
|||
|
|
level = logging.INFO,
|
|||
|
|
format = "%(asctime)s [%(levelname)s] %(name)s - %(message)s",
|
|||
|
|
)
|
|||
|
|
logger = logging.getLogger("main")
|
|||
|
|
|
|||
|
|
|
|||
|
|
# ─────────────────────────────────────────────────────────────────
|
|||
|
|
# 消息处理回调
|
|||
|
|
# ─────────────────────────────────────────────────────────────────
|
|||
|
|
|
|||
|
|
async def on_aircraft_status(msg: Message):
|
|||
|
|
logger.info(f"🛩️ 收到飞行状态 | {msg.topic} | {msg.payload}")
|
|||
|
|
|
|||
|
|
async def on_alert(msg: Message):
|
|||
|
|
logger.warning(f"🚨 告警消息 | priority={msg.priority.name} | {msg.payload}")
|
|||
|
|
|
|||
|
|
def on_telemetry_sync(msg: Message):
|
|||
|
|
"""同步处理器也被支持"""
|
|||
|
|
logger.info(f"📡 遥测数据 (sync) | {msg.payload}")
|
|||
|
|
|
|||
|
|
|
|||
|
|
# ─────────────────────────────────────────────────────────────────
|
|||
|
|
# 示例 1:Redis
|
|||
|
|
# ─────────────────────────────────────────────────────────────────
|
|||
|
|
|
|||
|
|
async def demo_redis():
|
|||
|
|
url = "redis://:mypassword@127.0.0.1:6379/0?socket_timeout=5"
|
|||
|
|
logger.info(f"\n{'═'*55}\n Redis 示例 url={url}\n{'═'*55}")
|
|||
|
|
|
|||
|
|
async with CommAdapterFactory.create(url) as adapter:
|
|||
|
|
# 订阅
|
|||
|
|
await adapter.subscribe("aircraft/status", on_aircraft_status)
|
|||
|
|
await adapter.subscribe("aircraft/alert", on_alert)
|
|||
|
|
|
|||
|
|
# 发布
|
|||
|
|
await adapter.publish(Message(
|
|||
|
|
topic = "aircraft/status",
|
|||
|
|
payload = {"id": "F-22", "speed": 2414, "altitude": 15000},
|
|||
|
|
))
|
|||
|
|
await adapter.publish(Message(
|
|||
|
|
topic = "aircraft/alert",
|
|||
|
|
payload = {"code": "E001", "msg": "发动机温度过高"},
|
|||
|
|
priority = MessagePriority.URGENT,
|
|||
|
|
))
|
|||
|
|
|
|||
|
|
await asyncio.sleep(0.5) # 等待消息到达
|
|||
|
|
|
|||
|
|
|
|||
|
|
# ─────────────────────────────────────────────────────────────────
|
|||
|
|
# 示例 2:Kafka
|
|||
|
|
# ─────────────────────────────────────────────────────────────────
|
|||
|
|
|
|||
|
|
async def demo_kafka():
|
|||
|
|
url = "kafka://broker:9092/flight-group?auto_offset_reset=earliest&acks=all"
|
|||
|
|
logger.info(f"\n{'═'*55}\n Kafka 示例 url={url}\n{'═'*55}")
|
|||
|
|
|
|||
|
|
async with CommAdapterFactory.create(url) as adapter:
|
|||
|
|
await adapter.subscribe("telemetry", on_telemetry_sync)
|
|||
|
|
|
|||
|
|
await adapter.publish(Message(
|
|||
|
|
topic = "telemetry",
|
|||
|
|
payload = {"aircraft": "J-20", "lat": 39.9, "lon": 116.4, "alt": 20000},
|
|||
|
|
headers = {"source": "radar-01"},
|
|||
|
|
))
|
|||
|
|
|
|||
|
|
await asyncio.sleep(1.0)
|
|||
|
|
|
|||
|
|
|
|||
|
|
# ─────────────────────────────────────────────────────────────────
|
|||
|
|
# 示例 3:DDS
|
|||
|
|
# ─────────────────────────────────────────────────────────────────
|
|||
|
|
|
|||
|
|
async def demo_dds():
|
|||
|
|
url = "dds://0@localhost:7400/rt?reliability=RELIABLE&durability=TRANSIENT_LOCAL"
|
|||
|
|
logger.info(f"\n{'═'*55}\n DDS 示例 url={url}\n{'═'*55}")
|
|||
|
|
|
|||
|
|
async with CommAdapterFactory.create(url) as adapter:
|
|||
|
|
await adapter.subscribe("command/fire", on_alert)
|
|||
|
|
|
|||
|
|
await adapter.publish(Message(
|
|||
|
|
topic = "command/fire",
|
|||
|
|
payload = {"weapon": "AIM-120", "target": "T001"},
|
|||
|
|
priority = MessagePriority.HIGH,
|
|||
|
|
))
|
|||
|
|
|
|||
|
|
await asyncio.sleep(0.3)
|
|||
|
|
|
|||
|
|
|
|||
|
|
# ─────────────────────────────────────────────────────────────────
|
|||
|
|
# 示例 4:扩展新协议(MQTT)
|
|||
|
|
# ─────────────────────────────────────────────────────────────────
|
|||
|
|
|
|||
|
|
async def demo_extend_mqtt():
|
|||
|
|
"""
|
|||
|
|
演示如何在运行时注册新协议,无需修改框架代码
|
|||
|
|
只需实现 CommBaseAdapter 子类并调用 register()
|
|||
|
|
"""
|
|||
|
|
from uas.comm.base import CommBaseAdapter, MessageHandler
|
|||
|
|
from uas.comm.message import Message
|
|||
|
|
|
|||
|
|
class MqttAdapter(CommBaseAdapter):
|
|||
|
|
"""MQTT 适配器骨架(实际使用 aiomqtt 库)"""
|
|||
|
|
async def connect(self):
|
|||
|
|
self._running = True
|
|||
|
|
self.logger.info(f"[MQTT] 连接到 {self.config.host}:{self.config.port}")
|
|||
|
|
async def disconnect(self):
|
|||
|
|
self._running = False
|
|||
|
|
async def publish(self, message: Message) -> bool:
|
|||
|
|
self.logger.info(f"[MQTT] 发布 {message.topic}: {message.payload}")
|
|||
|
|
return True
|
|||
|
|
async def subscribe(self, topic: str, handler: MessageHandler):
|
|||
|
|
self._register_handler(topic, handler)
|
|||
|
|
self.logger.info(f"[MQTT] 订阅 {topic}")
|
|||
|
|
async def unsubscribe(self, topic: str):
|
|||
|
|
self._handlers.pop(topic, None)
|
|||
|
|
|
|||
|
|
# 动态注册
|
|||
|
|
CommAdapterFactory.register("mqtt", MqttAdapter)
|
|||
|
|
|
|||
|
|
url = "mqtt://broker.hivemq.com:1883/aircraft?qos=1"
|
|||
|
|
async with CommAdapterFactory.create(url) as adapter:
|
|||
|
|
await adapter.subscribe("sensor/+/temp", on_telemetry_sync)
|
|||
|
|
await adapter.publish(Message(
|
|||
|
|
topic = "sensor/001/temp",
|
|||
|
|
payload = {"temp": 85.3},
|
|||
|
|
))
|
|||
|
|
|
|||
|
|
logger.info(f"当前已注册协议: {CommAdapterFactory.available_schemes()}")
|
|||
|
|
|
|||
|
|
|
|||
|
|
# ─────────────────────────────────────────────────────────────────
|
|||
|
|
# 主入口
|
|||
|
|
# ─────────────────────────────────────────────────────────────────
|
|||
|
|
|
|||
|
|
async def main():
|
|||
|
|
# 注意:Redis/Kafka 需要真实服务,DDS 在无 cyclonedds 时自动降级为模拟模式
|
|||
|
|
# await demo_redis()
|
|||
|
|
# await demo_kafka()
|
|||
|
|
await demo_dds()
|
|||
|
|
await demo_extend_mqtt()
|
|||
|
|
|
|||
|
|
if __name__ == "__main__":
|
|||
|
|
asyncio.run(main())
|