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