mirror of
https://github.com/dptech-corp/Uni-Lab-OS.git
synced 2025-12-19 14:01:20 +00:00
Compare commits
2 Commits
52544a2c69
...
v0.10.12
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b1cdef9185 | ||
|
|
9854ed8c9c |
@@ -1,6 +1,6 @@
|
|||||||
package:
|
package:
|
||||||
name: unilabos
|
name: unilabos
|
||||||
version: 0.10.11
|
version: 0.10.12
|
||||||
|
|
||||||
source:
|
source:
|
||||||
path: ../unilabos
|
path: ../unilabos
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
package:
|
package:
|
||||||
name: ros-humble-unilabos-msgs
|
name: ros-humble-unilabos-msgs
|
||||||
version: 0.10.11
|
version: 0.10.12
|
||||||
source:
|
source:
|
||||||
path: ../../unilabos_msgs
|
path: ../../unilabos_msgs
|
||||||
target_directory: src
|
target_directory: src
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
package:
|
package:
|
||||||
name: unilabos
|
name: unilabos
|
||||||
version: "0.10.11"
|
version: "0.10.12"
|
||||||
|
|
||||||
source:
|
source:
|
||||||
path: ../..
|
path: ../..
|
||||||
|
|||||||
2
setup.py
2
setup.py
@@ -4,7 +4,7 @@ package_name = 'unilabos'
|
|||||||
|
|
||||||
setup(
|
setup(
|
||||||
name=package_name,
|
name=package_name,
|
||||||
version='0.10.11',
|
version='0.10.12',
|
||||||
packages=find_packages(),
|
packages=find_packages(),
|
||||||
include_package_data=True,
|
include_package_data=True,
|
||||||
install_requires=['setuptools'],
|
install_requires=['setuptools'],
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
__version__ = "0.10.11"
|
__version__ = "0.10.12"
|
||||||
|
|||||||
@@ -141,7 +141,7 @@ class CommunicationClientFactory:
|
|||||||
"""
|
"""
|
||||||
if cls._client_cache is None:
|
if cls._client_cache is None:
|
||||||
cls._client_cache = cls.create_client(protocol)
|
cls._client_cache = cls.create_client(protocol)
|
||||||
logger.info(f"[CommunicationFactory] Created {type(cls._client_cache).__name__} client")
|
logger.trace(f"[CommunicationFactory] Created {type(cls._client_cache).__name__} client")
|
||||||
|
|
||||||
return cls._client_cache
|
return cls._client_cache
|
||||||
|
|
||||||
|
|||||||
@@ -389,7 +389,7 @@ class MessageProcessor:
|
|||||||
self.is_running = True
|
self.is_running = True
|
||||||
self.thread = threading.Thread(target=self._run, daemon=True, name="MessageProcessor")
|
self.thread = threading.Thread(target=self._run, daemon=True, name="MessageProcessor")
|
||||||
self.thread.start()
|
self.thread.start()
|
||||||
logger.info("[MessageProcessor] Started")
|
logger.trace("[MessageProcessor] Started")
|
||||||
|
|
||||||
def stop(self) -> None:
|
def stop(self) -> None:
|
||||||
"""停止消息处理线程"""
|
"""停止消息处理线程"""
|
||||||
@@ -939,7 +939,7 @@ class QueueProcessor:
|
|||||||
# 事件通知机制
|
# 事件通知机制
|
||||||
self.queue_update_event = threading.Event()
|
self.queue_update_event = threading.Event()
|
||||||
|
|
||||||
logger.info("[QueueProcessor] Initialized")
|
logger.trace("[QueueProcessor] Initialized")
|
||||||
|
|
||||||
def set_websocket_client(self, websocket_client: "WebSocketClient"):
|
def set_websocket_client(self, websocket_client: "WebSocketClient"):
|
||||||
"""设置WebSocket客户端引用"""
|
"""设置WebSocket客户端引用"""
|
||||||
@@ -954,7 +954,7 @@ class QueueProcessor:
|
|||||||
self.is_running = True
|
self.is_running = True
|
||||||
self.thread = threading.Thread(target=self._run, daemon=True, name="QueueProcessor")
|
self.thread = threading.Thread(target=self._run, daemon=True, name="QueueProcessor")
|
||||||
self.thread.start()
|
self.thread.start()
|
||||||
logger.info("[QueueProcessor] Started")
|
logger.trace("[QueueProcessor] Started")
|
||||||
|
|
||||||
def stop(self) -> None:
|
def stop(self) -> None:
|
||||||
"""停止队列处理线程"""
|
"""停止队列处理线程"""
|
||||||
|
|||||||
@@ -97,7 +97,7 @@ def canonicalize_nodes_data(
|
|||||||
|
|
||||||
for node in nodes:
|
for node in nodes:
|
||||||
try:
|
try:
|
||||||
print_status(f"DeviceId: {node['id']}, Class: {node['class']}", "info")
|
# print_status(f"DeviceId: {node['id']}, Class: {node['class']}", "info")
|
||||||
# 使用标准化方法
|
# 使用标准化方法
|
||||||
resource_instance = ResourceDictInstance.get_resource_instance_from_dict(node)
|
resource_instance = ResourceDictInstance.get_resource_instance_from_dict(node)
|
||||||
known_nodes[node["id"]] = resource_instance
|
known_nodes[node["id"]] = resource_instance
|
||||||
|
|||||||
@@ -582,7 +582,7 @@ class BaseROS2DeviceNode(Node, Generic[T]):
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.lab_logger().error(f"更新资源uuid失败: {e}")
|
self.lab_logger().error(f"更新资源uuid失败: {e}")
|
||||||
self.lab_logger().error(traceback.format_exc())
|
self.lab_logger().error(traceback.format_exc())
|
||||||
self.lab_logger().debug(f"资源更新结果: {response}")
|
self.lab_logger().trace(f"资源更新结果: {response}")
|
||||||
|
|
||||||
async def get_resource(self, resources_uuid: List[str], with_children: bool = True) -> ResourceTreeSet:
|
async def get_resource(self, resources_uuid: List[str], with_children: bool = True) -> ResourceTreeSet:
|
||||||
"""
|
"""
|
||||||
@@ -1164,7 +1164,6 @@ class BaseROS2DeviceNode(Node, Generic[T]):
|
|||||||
execution_error = traceback.format_exc()
|
execution_error = traceback.format_exc()
|
||||||
break
|
break
|
||||||
|
|
||||||
##### self.lab_logger().info(f"准备执行: {action_kwargs}, 函数: {ACTION.__name__}")
|
|
||||||
time_start = time.time()
|
time_start = time.time()
|
||||||
time_overall = 100
|
time_overall = 100
|
||||||
future = None
|
future = None
|
||||||
@@ -1172,35 +1171,36 @@ class BaseROS2DeviceNode(Node, Generic[T]):
|
|||||||
# 将阻塞操作放入线程池执行
|
# 将阻塞操作放入线程池执行
|
||||||
if asyncio.iscoroutinefunction(ACTION):
|
if asyncio.iscoroutinefunction(ACTION):
|
||||||
try:
|
try:
|
||||||
##### self.lab_logger().info(f"异步执行动作 {ACTION}")
|
self.lab_logger().trace(f"异步执行动作 {ACTION}")
|
||||||
future = ROS2DeviceNode.run_async_func(ACTION, trace_error=False, **action_kwargs)
|
def _handle_future_exception(fut: Future):
|
||||||
|
|
||||||
def _handle_future_exception(fut):
|
|
||||||
nonlocal execution_error, execution_success, action_return_value
|
nonlocal execution_error, execution_success, action_return_value
|
||||||
try:
|
try:
|
||||||
action_return_value = fut.result()
|
action_return_value = fut.result()
|
||||||
|
if isinstance(action_return_value, BaseException):
|
||||||
|
raise action_return_value
|
||||||
execution_success = True
|
execution_success = True
|
||||||
except Exception as e:
|
except Exception as _:
|
||||||
execution_error = traceback.format_exc()
|
execution_error = traceback.format_exc()
|
||||||
error(
|
error(
|
||||||
f"异步任务 {ACTION.__name__} 报错了\n{traceback.format_exc()}\n原始输入:{action_kwargs}"
|
f"异步任务 {ACTION.__name__} 报错了\n{traceback.format_exc()}\n原始输入:{action_kwargs}"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
future = ROS2DeviceNode.run_async_func(ACTION, trace_error=False, **action_kwargs)
|
||||||
future.add_done_callback(_handle_future_exception)
|
future.add_done_callback(_handle_future_exception)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
execution_error = traceback.format_exc()
|
execution_error = traceback.format_exc()
|
||||||
execution_success = False
|
execution_success = False
|
||||||
self.lab_logger().error(f"创建异步任务失败: {traceback.format_exc()}")
|
self.lab_logger().error(f"创建异步任务失败: {traceback.format_exc()}")
|
||||||
else:
|
else:
|
||||||
##### self.lab_logger().info(f"同步执行动作 {ACTION}")
|
self.lab_logger().trace(f"同步执行动作 {ACTION}")
|
||||||
future = self._executor.submit(ACTION, **action_kwargs)
|
future = self._executor.submit(ACTION, **action_kwargs)
|
||||||
|
|
||||||
def _handle_future_exception(fut):
|
def _handle_future_exception(fut: Future):
|
||||||
nonlocal execution_error, execution_success, action_return_value
|
nonlocal execution_error, execution_success, action_return_value
|
||||||
try:
|
try:
|
||||||
action_return_value = fut.result()
|
action_return_value = fut.result()
|
||||||
execution_success = True
|
execution_success = True
|
||||||
except Exception as e:
|
except Exception as _:
|
||||||
execution_error = traceback.format_exc()
|
execution_error = traceback.format_exc()
|
||||||
error(
|
error(
|
||||||
f"同步任务 {ACTION.__name__} 报错了\n{traceback.format_exc()}\n原始输入:{action_kwargs}"
|
f"同步任务 {ACTION.__name__} 报错了\n{traceback.format_exc()}\n原始输入:{action_kwargs}"
|
||||||
@@ -1305,7 +1305,7 @@ class BaseROS2DeviceNode(Node, Generic[T]):
|
|||||||
get_result_info_str(execution_error, execution_success, action_return_value),
|
get_result_info_str(execution_error, execution_success, action_return_value),
|
||||||
)
|
)
|
||||||
|
|
||||||
##### self.lab_logger().info(f"动作 {action_name} 完成并返回结果")
|
self.lab_logger().trace(f"动作 {action_name} 完成并返回结果")
|
||||||
return result_msg
|
return result_msg
|
||||||
|
|
||||||
return execute_callback
|
return execute_callback
|
||||||
@@ -1540,17 +1540,29 @@ class ROS2DeviceNode:
|
|||||||
这个类封装了设备类实例和ROS2节点的功能,提供ROS2接口。
|
这个类封装了设备类实例和ROS2节点的功能,提供ROS2接口。
|
||||||
它不继承设备类,而是通过代理模式访问设备类的属性和方法。
|
它不继承设备类,而是通过代理模式访问设备类的属性和方法。
|
||||||
"""
|
"""
|
||||||
|
@staticmethod
|
||||||
|
async def safe_task_wrapper(trace_callback, func, **kwargs):
|
||||||
|
try:
|
||||||
|
if callable(trace_callback):
|
||||||
|
trace_callback(await func(**kwargs))
|
||||||
|
return await func(**kwargs)
|
||||||
|
except Exception as e:
|
||||||
|
if callable(trace_callback):
|
||||||
|
trace_callback(e)
|
||||||
|
return e
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def run_async_func(cls, func, trace_error=True, **kwargs) -> Task:
|
def run_async_func(cls, func, trace_error=True, inner_trace_callback=None, **kwargs) -> Task:
|
||||||
def _handle_future_exception(fut):
|
def _handle_future_exception(fut: Future):
|
||||||
try:
|
try:
|
||||||
fut.result()
|
ret = fut.result()
|
||||||
|
if isinstance(ret, BaseException):
|
||||||
|
raise ret
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
error(f"异步任务 {func.__name__} 报错了")
|
error(f"异步任务 {func.__name__} 获取结果失败")
|
||||||
error(traceback.format_exc())
|
error(traceback.format_exc())
|
||||||
|
|
||||||
future = rclpy.get_global_executor().create_task(func(**kwargs))
|
future = rclpy.get_global_executor().create_task(ROS2DeviceNode.safe_task_wrapper(inner_trace_callback, func, **kwargs))
|
||||||
if trace_error:
|
if trace_error:
|
||||||
future.add_done_callback(_handle_future_exception)
|
future.add_done_callback(_handle_future_exception)
|
||||||
return future
|
return future
|
||||||
|
|||||||
@@ -718,7 +718,7 @@ class HostNode(BaseROS2DeviceNode):
|
|||||||
feedback_callback=lambda feedback_msg: self.feedback_callback(item, action_id, feedback_msg),
|
feedback_callback=lambda feedback_msg: self.feedback_callback(item, action_id, feedback_msg),
|
||||||
goal_uuid=goal_uuid_obj,
|
goal_uuid=goal_uuid_obj,
|
||||||
)
|
)
|
||||||
future.add_done_callback(lambda future: self.goal_response_callback(item, action_id, future))
|
future.add_done_callback(lambda f: self.goal_response_callback(item, action_id, f))
|
||||||
|
|
||||||
def goal_response_callback(self, item: "QueueItem", action_id: str, future) -> None:
|
def goal_response_callback(self, item: "QueueItem", action_id: str, future) -> None:
|
||||||
"""目标响应回调"""
|
"""目标响应回调"""
|
||||||
@@ -729,9 +729,11 @@ class HostNode(BaseROS2DeviceNode):
|
|||||||
|
|
||||||
self.lab_logger().info(f"[Host Node] Goal {action_id} ({item.job_id}) accepted")
|
self.lab_logger().info(f"[Host Node] Goal {action_id} ({item.job_id}) accepted")
|
||||||
self._goals[item.job_id] = goal_handle
|
self._goals[item.job_id] = goal_handle
|
||||||
goal_handle.get_result_async().add_done_callback(
|
goal_future = goal_handle.get_result_async()
|
||||||
lambda future: self.get_result_callback(item, action_id, future)
|
goal_future.add_done_callback(
|
||||||
|
lambda f: self.get_result_callback(item, action_id, f)
|
||||||
)
|
)
|
||||||
|
goal_future.result()
|
||||||
|
|
||||||
def feedback_callback(self, item: "QueueItem", action_id: str, feedback_msg) -> None:
|
def feedback_callback(self, item: "QueueItem", action_id: str, feedback_msg) -> None:
|
||||||
"""反馈回调"""
|
"""反馈回调"""
|
||||||
|
|||||||
@@ -155,7 +155,7 @@ class ResourceDictInstance(object):
|
|||||||
res_dict = self.res_content.model_dump(by_alias=True)
|
res_dict = self.res_content.model_dump(by_alias=True)
|
||||||
res_dict["children"] = {child.res_content.id: child.get_plr_nested_dict() for child in self.children}
|
res_dict["children"] = {child.res_content.id: child.get_plr_nested_dict() for child in self.children}
|
||||||
res_dict["parent"] = self.res_content.parent_instance_name
|
res_dict["parent"] = self.res_content.parent_instance_name
|
||||||
res_dict["position"] = self.res_content.position.position.model_dump()
|
res_dict["position"] = self.res_content.pose.position.model_dump()
|
||||||
del res_dict["pose"]
|
del res_dict["pose"]
|
||||||
return res_dict
|
return res_dict
|
||||||
|
|
||||||
|
|||||||
@@ -162,8 +162,9 @@ def configure_logger(loglevel=None, working_dir=None):
|
|||||||
"""
|
"""
|
||||||
# 获取根日志记录器
|
# 获取根日志记录器
|
||||||
root_logger = logging.getLogger()
|
root_logger = logging.getLogger()
|
||||||
|
root_logger.setLevel(TRACE_LEVEL)
|
||||||
# 设置日志级别
|
# 设置日志级别
|
||||||
|
numeric_level = logging.DEBUG
|
||||||
if loglevel is not None:
|
if loglevel is not None:
|
||||||
if isinstance(loglevel, str):
|
if isinstance(loglevel, str):
|
||||||
# 将字符串转换为logging级别
|
# 将字符串转换为logging级别
|
||||||
@@ -173,12 +174,8 @@ def configure_logger(loglevel=None, working_dir=None):
|
|||||||
numeric_level = getattr(logging, loglevel.upper(), None)
|
numeric_level = getattr(logging, loglevel.upper(), None)
|
||||||
if not isinstance(numeric_level, int):
|
if not isinstance(numeric_level, int):
|
||||||
print(f"警告: 无效的日志级别 '{loglevel}',使用默认级别 DEBUG")
|
print(f"警告: 无效的日志级别 '{loglevel}',使用默认级别 DEBUG")
|
||||||
numeric_level = logging.DEBUG
|
|
||||||
else:
|
else:
|
||||||
numeric_level = loglevel
|
numeric_level = loglevel
|
||||||
root_logger.setLevel(numeric_level)
|
|
||||||
else:
|
|
||||||
root_logger.setLevel(logging.DEBUG) # 默认级别
|
|
||||||
|
|
||||||
# 移除已存在的处理器
|
# 移除已存在的处理器
|
||||||
for handler in root_logger.handlers[:]:
|
for handler in root_logger.handlers[:]:
|
||||||
@@ -186,7 +183,7 @@ def configure_logger(loglevel=None, working_dir=None):
|
|||||||
|
|
||||||
# 创建控制台处理器
|
# 创建控制台处理器
|
||||||
console_handler = logging.StreamHandler()
|
console_handler = logging.StreamHandler()
|
||||||
console_handler.setLevel(root_logger.level) # 使用与根记录器相同的级别
|
console_handler.setLevel(numeric_level) # 使用与根记录器相同的级别
|
||||||
|
|
||||||
# 使用自定义的颜色格式化器
|
# 使用自定义的颜色格式化器
|
||||||
color_formatter = ColoredFormatter()
|
color_formatter = ColoredFormatter()
|
||||||
@@ -206,7 +203,7 @@ def configure_logger(loglevel=None, working_dir=None):
|
|||||||
|
|
||||||
# 创建文件处理器
|
# 创建文件处理器
|
||||||
file_handler = logging.FileHandler(log_filepath, encoding="utf-8")
|
file_handler = logging.FileHandler(log_filepath, encoding="utf-8")
|
||||||
file_handler.setLevel(root_logger.level)
|
file_handler.setLevel(TRACE_LEVEL)
|
||||||
|
|
||||||
# 使用不带颜色的格式化器
|
# 使用不带颜色的格式化器
|
||||||
file_formatter = ColoredFormatter(use_colors=False)
|
file_formatter = ColoredFormatter(use_colors=False)
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
<?xml-model href="http://download.ros.org/schema/package_format3.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
|
<?xml-model href="http://download.ros.org/schema/package_format3.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
|
||||||
<package format="3">
|
<package format="3">
|
||||||
<name>unilabos_msgs</name>
|
<name>unilabos_msgs</name>
|
||||||
<version>0.10.11</version>
|
<version>0.10.12</version>
|
||||||
<description>ROS2 Messages package for unilabos devices</description>
|
<description>ROS2 Messages package for unilabos devices</description>
|
||||||
<maintainer email="changjh@pku.edu.cn">Junhan Chang</maintainer>
|
<maintainer email="changjh@pku.edu.cn">Junhan Chang</maintainer>
|
||||||
<maintainer email="18435084+Xuwznln@users.noreply.github.com">Xuwznln</maintainer>
|
<maintainer email="18435084+Xuwznln@users.noreply.github.com">Xuwznln</maintainer>
|
||||||
|
|||||||
Reference in New Issue
Block a user