mirror of
https://github.com/dptech-corp/Uni-Lab-OS.git
synced 2026-02-05 05:45:10 +00:00
container 添加和更新完成
This commit is contained in:
@@ -8,18 +8,21 @@ from unilabos.ros.msgs.message_converter import convert_from_ros_msg
|
||||
class RegularContainer(object):
|
||||
# 第一个参数必须是id传入
|
||||
# noinspection PyShadowingBuiltins
|
||||
def __init__(self, id: str, data: dict = None):
|
||||
def __init__(self, id: str):
|
||||
self.id = id
|
||||
self.ulr_resource = Resource()
|
||||
self.ulr_resource_data = data
|
||||
self._data = None
|
||||
|
||||
@property
|
||||
def ulr_resource_data(self):
|
||||
return json.loads(self.ulr_resource.data) if self.ulr_resource.data else {}
|
||||
if self._data is None:
|
||||
self._data = json.loads(self.ulr_resource.data) if self.ulr_resource.data else {}
|
||||
return self._data
|
||||
|
||||
@ulr_resource_data.setter
|
||||
def ulr_resource_data(self, value: dict):
|
||||
self.ulr_resource.data = json.dumps(value)
|
||||
self._data = value
|
||||
self.ulr_resource.data = json.dumps(self._data)
|
||||
|
||||
@property
|
||||
def liquid_type(self):
|
||||
@@ -48,6 +51,7 @@ class RegularContainer(object):
|
||||
获取UlrResource对象
|
||||
:return: UlrResource对象
|
||||
"""
|
||||
self.ulr_resource_data = self.ulr_resource_data # 确保数据被更新
|
||||
return self.ulr_resource
|
||||
|
||||
def get_ulr_resource_as_dict(self) -> Resource:
|
||||
@@ -55,7 +59,9 @@ class RegularContainer(object):
|
||||
获取UlrResource对象
|
||||
:return: UlrResource对象
|
||||
"""
|
||||
return convert_from_ros_msg(self.ulr_resource)
|
||||
to_dict = convert_from_ros_msg(self.get_ulr_resource())
|
||||
to_dict["type"] = "container"
|
||||
return to_dict
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.id}"
|
||||
@@ -4,8 +4,10 @@ import json
|
||||
from typing import Union
|
||||
import numpy as np
|
||||
import networkx as nx
|
||||
from unilabos_msgs.msg import Resource
|
||||
|
||||
from unilabos.resources.container import RegularContainer
|
||||
from unilabos.ros.msgs.message_converter import convert_from_ros_msg_with_mapping, convert_to_ros_msg
|
||||
|
||||
try:
|
||||
from pylabrobot.resources.resource import Resource as ResourcePLR
|
||||
@@ -469,7 +471,8 @@ def initialize_resource(resource_config: dict) -> list[dict]:
|
||||
r["position"] = resource_config["position"]
|
||||
r = tree_to_list([r])
|
||||
elif resource_class_config["type"] == "unilabos":
|
||||
res_instance: RegularContainer = RESOURCE(id=resource_config["name"], data=resource_config.get("data", {}))
|
||||
res_instance: RegularContainer = RESOURCE(id=resource_config["name"])
|
||||
res_instance.ulr_resource = convert_to_ros_msg(Resource, {k:v for k,v in resource_config.items() if k != "class"})
|
||||
r = [res_instance.get_ulr_resource_as_dict()]
|
||||
elif isinstance(RESOURCE, dict):
|
||||
r = [RESOURCE.copy()]
|
||||
|
||||
@@ -19,6 +19,7 @@ from rclpy.service import Service
|
||||
from unilabos_msgs.action import SendCmd
|
||||
from unilabos_msgs.srv._serial_command import SerialCommand_Request, SerialCommand_Response
|
||||
|
||||
from unilabos.resources.container import RegularContainer
|
||||
from unilabos.resources.graphio import (
|
||||
convert_resources_to_type,
|
||||
convert_resources_from_type,
|
||||
@@ -343,9 +344,8 @@ class BaseROS2DeviceNode(Node, Generic[T]):
|
||||
ADD_LIQUID_TYPE = other_calling_param.pop("ADD_LIQUID_TYPE", [])
|
||||
LIQUID_VOLUME = other_calling_param.pop("LIQUID_VOLUME", [])
|
||||
LIQUID_INPUT_SLOT = other_calling_param.pop("LIQUID_INPUT_SLOT", [])
|
||||
if len(LIQUID_INPUT_SLOT) and LIQUID_INPUT_SLOT[0] == -1:
|
||||
print("create container")
|
||||
slot = other_calling_param.pop("slot", "-1")
|
||||
resource = None
|
||||
if slot != "-1": # slot为负数的时候采用assign方法
|
||||
other_calling_param["slot"] = slot
|
||||
# 本地拿到这个物料,可能需要先做初始化?
|
||||
@@ -364,6 +364,28 @@ class BaseROS2DeviceNode(Node, Generic[T]):
|
||||
if initialize_full:
|
||||
resources = initialize_resources([resources])
|
||||
request.resources = [convert_to_ros_msg(Resource, resources)]
|
||||
if len(LIQUID_INPUT_SLOT) and LIQUID_INPUT_SLOT[0] == -1:
|
||||
container_instance = request.resources[0]
|
||||
container_query_dict: dict = resources
|
||||
found_resources = self.resource_tracker.figure_resource({"id": container_query_dict["name"]}, try_mode=True)
|
||||
if not len(found_resources):
|
||||
self.resource_tracker.add_resource(container_instance)
|
||||
logger.info(f"添加物料{container_query_dict['name']}到资源跟踪器")
|
||||
else:
|
||||
assert len(found_resources) == 1, f"找到多个同名物料: {container_query_dict['name']}, 请检查物料系统"
|
||||
resource = found_resources[0]
|
||||
if isinstance(resource, Resource):
|
||||
regular_container = RegularContainer(resource.id)
|
||||
regular_container.ulr_resource = resource
|
||||
regular_container.ulr_resource_data.update(json.loads(container_instance.data))
|
||||
logger.info(f"更新物料{container_query_dict['name']}的数据{resource.data} ULR")
|
||||
elif isinstance(resource, dict):
|
||||
if "data" not in resource:
|
||||
resource["data"] = {}
|
||||
resource["data"].update(json.loads(container_instance.data))
|
||||
logger.info(f"更新物料{container_query_dict['name']}的数据{resource['data']} dict")
|
||||
else:
|
||||
logger.info(f"更新物料{container_query_dict['name']}出现不支持的数据类型{type(resource)} {resource}")
|
||||
response = rclient.call(request)
|
||||
# 应该先add_resource了
|
||||
res.response = "OK"
|
||||
@@ -387,7 +409,8 @@ class BaseROS2DeviceNode(Node, Generic[T]):
|
||||
res.response = serialize_result_info(traceback.format_exc(), False, {})
|
||||
return res
|
||||
# 接下来该根据bind_parent_id进行assign了,目前只有plr可以进行assign,不然没有办法输入到物料系统中
|
||||
resource = self.resource_tracker.figure_resource({"name": bind_parent_id})
|
||||
if bind_parent_id != self.node_name:
|
||||
resource = self.resource_tracker.figure_resource({"name": bind_parent_id}) # 拿到父节点,进行具体assign等操作
|
||||
# request.resources = [convert_to_ros_msg(Resource, resources)]
|
||||
|
||||
try:
|
||||
@@ -437,7 +460,7 @@ class BaseROS2DeviceNode(Node, Generic[T]):
|
||||
"bind_parent_id": bind_parent_id,
|
||||
}
|
||||
)
|
||||
future = action_client.send_goal_async(goal, goal_uuid=uuid.uuid4())
|
||||
future = action_client.send_goal_async(goal)
|
||||
|
||||
def done_cb(*args):
|
||||
self.lab_logger().info(f"向meshmanager发送新增resource完成")
|
||||
@@ -903,9 +926,9 @@ class ROS2DeviceNode:
|
||||
from unilabos.ros.nodes.presets.protocol_node import ROS2ProtocolNode
|
||||
|
||||
if self._driver_class is ROS2ProtocolNode:
|
||||
self._driver_creator = ProtocolNodeCreator(driver_class, children=children)
|
||||
self._driver_creator = ProtocolNodeCreator(driver_class, children=children, resource_tracker=self.resource_tracker)
|
||||
else:
|
||||
self._driver_creator = DeviceClassCreator(driver_class)
|
||||
self._driver_creator = DeviceClassCreator(driver_class, children=children, resource_tracker=self.resource_tracker)
|
||||
|
||||
if driver_is_ros:
|
||||
driver_params["device_id"] = device_id
|
||||
|
||||
@@ -25,7 +25,7 @@ class DeviceNodeResourceTracker(object):
|
||||
def clear_resource(self):
|
||||
self.resources = []
|
||||
|
||||
def figure_resource(self, query_resource):
|
||||
def figure_resource(self, query_resource, try_mode=False):
|
||||
if isinstance(query_resource, list):
|
||||
return [self.figure_resource(r) for r in query_resource]
|
||||
res_id = query_resource.id if hasattr(query_resource, "id") else (query_resource.get("id") if isinstance(query_resource, dict) else None)
|
||||
@@ -45,10 +45,14 @@ class DeviceNodeResourceTracker(object):
|
||||
res_list.extend(
|
||||
self.loop_find_resource(r, resource_cls_type, identifier_key, getattr(query_resource, identifier_key))
|
||||
)
|
||||
assert len(res_list) == 1, f"{query_resource} 找到多个资源,请检查资源是否唯一: {res_list}"
|
||||
if not try_mode:
|
||||
assert len(res_list) > 0, f"没有找到资源 {query_resource},请检查资源是否存在"
|
||||
assert len(res_list) == 1, f"{query_resource} 找到多个资源,请检查资源是否唯一: {res_list}"
|
||||
else:
|
||||
return [i[1] for i in res_list]
|
||||
# 后续加入其他对比方式
|
||||
self.resource2parent_resource[id(query_resource)] = res_list[0][0]
|
||||
self.resource2parent_resource[id(res_list[0][1])] = res_list[0][0]
|
||||
# 后续加入其他对比方式
|
||||
return res_list[0][1]
|
||||
|
||||
def loop_find_resource(self, resource, target_resource_cls_type, identifier_key, compare_value, parent_res=None) -> List[Tuple[Any, Any]]:
|
||||
@@ -57,8 +61,12 @@ class DeviceNodeResourceTracker(object):
|
||||
children = getattr(resource, "children", [])
|
||||
for child in children:
|
||||
res_list.extend(self.loop_find_resource(child, target_resource_cls_type, identifier_key, compare_value, resource))
|
||||
if target_resource_cls_type == type(resource) or target_resource_cls_type == dict:
|
||||
if hasattr(resource, identifier_key):
|
||||
if target_resource_cls_type == type(resource):
|
||||
if target_resource_cls_type == dict:
|
||||
if identifier_key in resource:
|
||||
if resource[identifier_key] == compare_value:
|
||||
res_list.append((parent_res, resource))
|
||||
elif hasattr(resource, identifier_key):
|
||||
if getattr(resource, identifier_key) == compare_value:
|
||||
res_list.append((parent_res, resource))
|
||||
return res_list
|
||||
|
||||
@@ -33,7 +33,7 @@ class DeviceClassCreator(Generic[T]):
|
||||
这个类提供了从任意类创建实例的通用方法。
|
||||
"""
|
||||
|
||||
def __init__(self, cls: Type[T]):
|
||||
def __init__(self, cls: Type[T], children: Dict[str, Any], resource_tracker: DeviceNodeResourceTracker):
|
||||
"""
|
||||
初始化设备类创建器
|
||||
|
||||
@@ -42,6 +42,18 @@ class DeviceClassCreator(Generic[T]):
|
||||
"""
|
||||
self.device_cls = cls
|
||||
self.device_instance: Optional[T] = None
|
||||
self.children = children
|
||||
self.resource_tracker = resource_tracker
|
||||
|
||||
def attach_resource(self):
|
||||
"""
|
||||
附加资源到设备类实例
|
||||
"""
|
||||
if self.device_instance is not None:
|
||||
for c in self.children.values():
|
||||
if c["type"] == "container":
|
||||
self.resource_tracker.add_resource(c)
|
||||
|
||||
|
||||
def create_instance(self, data: Dict[str, Any]) -> T:
|
||||
"""
|
||||
@@ -60,6 +72,7 @@ class DeviceClassCreator(Generic[T]):
|
||||
}
|
||||
)
|
||||
self.post_create()
|
||||
self.attach_resource()
|
||||
return self.device_instance
|
||||
|
||||
def get_instance(self) -> Optional[T]:
|
||||
@@ -90,14 +103,15 @@ class PyLabRobotCreator(DeviceClassCreator[T]):
|
||||
cls: PyLabRobot设备类
|
||||
children: 子资源字典,用于资源替换
|
||||
"""
|
||||
super().__init__(cls)
|
||||
self.children = children
|
||||
self.resource_tracker = resource_tracker
|
||||
super().__init__(cls, children, resource_tracker)
|
||||
# 检查类是否具有deserialize方法
|
||||
self.has_deserialize = hasattr(cls, "deserialize") and callable(getattr(cls, "deserialize"))
|
||||
if not self.has_deserialize:
|
||||
logger.warning(f"类 {cls.__name__} 没有deserialize方法,将使用标准构造函数")
|
||||
|
||||
def attach_resource(self):
|
||||
pass # 只能增加实例化物料,原来默认物料仅为字典查询
|
||||
|
||||
def _process_resource_mapping(self, resource, source_type):
|
||||
if source_type == dict:
|
||||
from pylabrobot.resources.resource import Resource
|
||||
@@ -260,7 +274,7 @@ class ProtocolNodeCreator(DeviceClassCreator[T]):
|
||||
这个类提供了针对ProtocolNode设备类的实例创建方法,处理children参数。
|
||||
"""
|
||||
|
||||
def __init__(self, cls: Type[T], children: Dict[str, Any]):
|
||||
def __init__(self, cls: Type[T], children: Dict[str, Any], resource_tracker: DeviceNodeResourceTracker):
|
||||
"""
|
||||
初始化ProtocolNode设备类创建器
|
||||
|
||||
@@ -268,8 +282,7 @@ class ProtocolNodeCreator(DeviceClassCreator[T]):
|
||||
cls: ProtocolNode设备类
|
||||
children: 子资源字典,用于资源替换
|
||||
"""
|
||||
super().__init__(cls)
|
||||
self.children = children
|
||||
super().__init__(cls, children, resource_tracker)
|
||||
|
||||
def create_instance(self, data: Dict[str, Any]) -> T:
|
||||
"""
|
||||
@@ -282,8 +295,7 @@ class ProtocolNodeCreator(DeviceClassCreator[T]):
|
||||
ProtocolNode设备类实例
|
||||
"""
|
||||
try:
|
||||
|
||||
# 创建实例
|
||||
# 创建实例,额外补充一个给protocol node的字段,后面考虑取消
|
||||
data["children"] = self.children
|
||||
self.device_instance = super(ProtocolNodeCreator, self).create_instance(data)
|
||||
self.post_create()
|
||||
|
||||
Reference in New Issue
Block a user