base_agent/uas/comm/base.py

122 lines
3.9 KiB
Python
Raw Normal View History

2026-06-18 03:28:14 +00:00
# -*- coding: utf-8 -*-
"""
通信适配器抽象基类
所有中间件适配器必须继承此类并实现全部抽象方法
遵循 Factory + Strategy 模式
"""
import asyncio
import logging
from abc import ABC, abstractmethod
from dataclasses import dataclass
from typing import Callable, Awaitable, List, Optional
from uas.comm.message import Message
logger = logging.getLogger(__name__)
# 消息处理器类型:同步 or 异步均可
MessageHandler = Callable[[Message], Optional[Awaitable[None]]]
@dataclass
class ConnectionConfig:
"""通用连接配置(由 URL 解析填充)"""
scheme: str # redis / kafka / dds
host: str = "localhost"
port: int = 0
username: str = ""
password: str = ""
database: str = "" # redis db index / kafka group id / dds domain
options: dict = None # URL query 参数
def __post_init__(self):
if self.options is None:
self.options = {}
class CommBaseAdapter(ABC):
"""
所有通信适配器的抽象基类
子类只需实现以下 5 个核心方法
"""
def __init__(self, config: ConnectionConfig):
self.config = config
self.logger = logging.getLogger(self.__class__.__name__)
self._running = False
self._handlers: dict[str, List[MessageHandler]] = {}
# ── 生命周期 ──────────────────────────────────────────────────
@abstractmethod
async def connect(self) -> None:
"""建立连接"""
...
@abstractmethod
async def disconnect(self) -> None:
"""断开连接"""
...
@property
def is_connected(self) -> bool:
return self._running
# ── 核心功能 ──────────────────────────────────────────────────
@abstractmethod
async def publish(self, message: Message) -> bool:
"""
发布消息到指定 topic
:return: True 表示发送成功
"""
...
@abstractmethod
async def subscribe(self, topic: str, handler: MessageHandler) -> None:
"""
订阅 topic注册消息处理回调
支持通配符由各适配器自行实现
"""
...
@abstractmethod
async def unsubscribe(self, topic: str) -> None:
"""取消订阅"""
...
# ── 可选扩展 ──────────────────────────────────────────────────
async def request(self, message: Message, timeout: float = 5.0) -> Optional[Message]:
"""
Request/Reply 模式默认未实现子类可覆盖
"""
raise NotImplementedError(f"{self.__class__.__name__} 不支持 Request/Reply 模式")
async def health_check(self) -> bool:
"""健康检查,默认返回连接状态"""
return self._running
# ── 内部工具 ──────────────────────────────────────────────────
def _register_handler(self, topic: str, handler: MessageHandler):
self._handlers.setdefault(topic, []).append(handler)
async def _dispatch(self, message: Message):
"""将消息分发给所有匹配的 handler"""
handlers = self._handlers.get(message.topic, [])
for handler in handlers:
try:
result = handler(message)
if asyncio.iscoroutine(result):
await result
except Exception as e:
self.logger.error(f"Handler 执行异常 topic={message.topic}: {e}", exc_info=True)
async def __aenter__(self):
await self.connect()
return self
async def __aexit__(self, *args):
await self.disconnect()