mirror of
https://github.com/deepmodeling/Uni-Lab-OS
synced 2026-03-24 04:10:33 +00:00
fast registry load minor fix on skill & registry stripe ros2 schema desc add create-device-skill new registry system backwards to yaml remove not exist resource new registry sys exp. support with add device add ai conventions correct raise create resource error ret info fix revert ret info fix fix prcxi check add create_resource schema re signal host ready event add websocket connection timeout and improve reconnection logic add open_timeout parameter to websocket connection add TimeoutError and InvalidStatus exception handling implement exponential backoff for reconnection attempts simplify reconnection logic flow add gzip change pose extra to any add isFlapY
100 lines
3.0 KiB
Python
100 lines
3.0 KiB
Python
from functools import wraps
|
||
from typing import Any, Callable, Optional, TypeVar
|
||
|
||
F = TypeVar("F", bound=Callable[..., Any])
|
||
|
||
|
||
def singleton(cls):
|
||
"""
|
||
单例装饰器
|
||
确保被装饰的类只有一个实例
|
||
"""
|
||
instances = {}
|
||
|
||
def get_instance(*args, **kwargs):
|
||
if cls not in instances:
|
||
instances[cls] = cls(*args, **kwargs)
|
||
return instances[cls]
|
||
|
||
return get_instance
|
||
|
||
|
||
def subscribe(
|
||
topic: str,
|
||
msg_type: Optional[type] = None,
|
||
qos: int = 10,
|
||
) -> Callable[[F], F]:
|
||
"""
|
||
Topic订阅装饰器
|
||
|
||
用于装饰 driver 类中的方法,使其成为 ROS topic 的订阅回调。
|
||
当 ROS2DeviceNode 初始化时,会自动扫描并创建对应的订阅者。
|
||
|
||
Args:
|
||
topic: Topic 名称模板,支持以下占位符:
|
||
- {device_id}: 设备ID (如 "pump_1")
|
||
- {namespace}: 完整命名空间 (如 "/devices/pump_1")
|
||
msg_type: ROS 消息类型。如果为 None,需要在回调函数的类型注解中指定
|
||
qos: QoS 深度配置,默认为 10
|
||
"""
|
||
|
||
def decorator(func: F) -> F:
|
||
@wraps(func)
|
||
def wrapper(*args, **kwargs):
|
||
return func(*args, **kwargs)
|
||
|
||
wrapper._subscribe_topic = topic # type: ignore[attr-defined]
|
||
wrapper._subscribe_msg_type = msg_type # type: ignore[attr-defined]
|
||
wrapper._subscribe_qos = qos # type: ignore[attr-defined]
|
||
wrapper._has_subscribe = True # type: ignore[attr-defined]
|
||
|
||
return wrapper # type: ignore[return-value]
|
||
|
||
return decorator
|
||
|
||
|
||
def get_subscribe_config(func) -> dict:
|
||
"""获取函数上的订阅配置 (topic, msg_type, qos)"""
|
||
if hasattr(func, "_has_subscribe") and getattr(func, "_has_subscribe", False):
|
||
return {
|
||
"topic": getattr(func, "_subscribe_topic", None),
|
||
"msg_type": getattr(func, "_subscribe_msg_type", None),
|
||
"qos": getattr(func, "_subscribe_qos", 10),
|
||
}
|
||
return {}
|
||
|
||
|
||
def get_all_subscriptions(instance) -> list:
|
||
"""
|
||
扫描实例的所有方法,获取带有 @subscribe 装饰器的方法及其配置
|
||
|
||
Returns:
|
||
包含 (method_name, method, config) 元组的列表
|
||
"""
|
||
subscriptions = []
|
||
for attr_name in dir(instance):
|
||
if attr_name.startswith("_"):
|
||
continue
|
||
try:
|
||
attr = getattr(instance, attr_name)
|
||
if callable(attr):
|
||
config = get_subscribe_config(attr)
|
||
if config:
|
||
subscriptions.append((attr_name, attr, config))
|
||
except Exception:
|
||
pass
|
||
return subscriptions
|
||
|
||
|
||
# ---------------------------------------------------------------------------
|
||
# 向后兼容重导出 -- 已迁移到 unilabos.registry.decorators
|
||
# ---------------------------------------------------------------------------
|
||
from unilabos.registry.decorators import ( # noqa: E402, F401
|
||
topic_config,
|
||
get_topic_config,
|
||
always_free,
|
||
is_always_free,
|
||
not_action,
|
||
is_not_action,
|
||
)
|