diff --git a/test/commands/resource_add.md b/test/commands/resource_add.md index d80e155..84f5232 100644 --- a/test/commands/resource_add.md +++ b/test/commands/resource_add.md @@ -2,4 +2,10 @@ ```bash ros2 action send_goal /devices/host_node/create_resource_detailed unilabos_msgs/action/_resource_create_from_outer/ResourceCreateFromOuter "{ resources: [ { 'category': '', 'children': [], 'config': { 'type': 'Well', 'size_x': 6.86, 'size_y': 6.86, 'size_z': 10.67, 'rotation': { 'x': 0, 'y': 0, 'z': 0, 'type': 'Rotation' }, 'category': 'well', 'model': null, 'max_volume': 360, 'material_z_thickness': 0.5, 'compute_volume_from_height': null, 'compute_height_from_volume': null, 'bottom_type': 'flat', 'cross_section_type': 'circle' }, 'data': { 'liquids': [], 'pending_liquids': [], 'liquid_history': [] }, 'id': 'plate_well_11_7', 'name': 'plate_well_11_7', 'pose': { 'orientation': { 'w': 1.0, 'x': 0.0, 'y': 0.0, 'z': 0.0 }, 'position': { 'x': 0.0, 'y': 0.0, 'z': 0.0 } }, 'sample_id': '', 'parent': 'plate', 'type': 'device' } ], device_ids: [ 'PLR_STATION' ], bind_parent_ids: [ 'plate' ], bind_locations: [ { 'x': 0.0, 'y': 0.0, 'z': 0.0 } ], other_calling_params: [ '{}' ] }" +``` + +使用mock_all.json启动,重新捕获MockContainerForChiller1 + +```bash +ros2 action send_goal /devices/host_node/create_resource unilabos_msgs/action/_resource_create_from_outer_easy/ResourceCreateFromOuterEasy "{ 'res_id': 'MockContainerForChiller1', 'device_id': 'MockChiller1', 'class_name': 'container', 'parent': 'MockChiller1', 'bind_locations': { 'x': 0.0, 'y': 0.0, 'z': 0.0 }, 'liquid_input_slot': [ -1 ], 'liquid_type': [ 'CuCl2' ], 'liquid_volume': [ 100.0 ], 'slot_on_deck': '' }" ``` \ No newline at end of file diff --git a/test/experiments/mock_devices/mock_all.json b/test/experiments/mock_devices/mock_all.json index f263b47..621690d 100644 --- a/test/experiments/mock_devices/mock_all.json +++ b/test/experiments/mock_devices/mock_all.json @@ -3,7 +3,9 @@ { "id": "MockChiller1", "name": "模拟冷却器", - "children": [], + "children": [ + + ], "parent": null, "type": "device", "class": "mock_chiller", @@ -25,6 +27,22 @@ "purpose": "" } }, + { + "id": "MockContainerForChiller1", + "name": "模拟容器", + "type": "container", + "parent": "MockChiller1", + "position": { + "x": 5, + "y": 0, + "z": 0 + }, + "data": { + "liquid_type": "CuCl2", + "liquid_volume": "100" + }, + "children": [] + }, { "id": "MockFilter1", "name": "模拟过滤器", diff --git a/unilabos/registry/resources/opentrons/container.yaml b/unilabos/registry/resources/opentrons/container.yaml new file mode 100644 index 0000000..c64b45f --- /dev/null +++ b/unilabos/registry/resources/opentrons/container.yaml @@ -0,0 +1,5 @@ +container: + description: regular organic container + class: + module: unilabos.resources.container:RegularContainer + type: unilabos diff --git a/unilabos/resources/container.py b/unilabos/resources/container.py new file mode 100644 index 0000000..a3f8b88 --- /dev/null +++ b/unilabos/resources/container.py @@ -0,0 +1,61 @@ +import json + +from unilabos_msgs.msg import Resource + +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): + self.id = id + self.ulr_resource = Resource() + self.ulr_resource_data = data + + @property + def ulr_resource_data(self): + return json.loads(self.ulr_resource.data) if self.ulr_resource.data else {} + + @ulr_resource_data.setter + def ulr_resource_data(self, value: dict): + self.ulr_resource.data = json.dumps(value) + + @property + def liquid_type(self): + return self.ulr_resource_data.get("liquid_type", None) + + @liquid_type.setter + def liquid_type(self, value: str): + if value is not None: + self.ulr_resource_data["liquid_type"] = value + else: + self.ulr_resource_data.pop("liquid_type", None) + + @property + def liquid_volume(self): + return self.ulr_resource_data.get("liquid_volume", None) + + @liquid_volume.setter + def liquid_volume(self, value: float): + if value is not None: + self.ulr_resource_data["liquid_volume"] = value + else: + self.ulr_resource_data.pop("liquid_volume", None) + + def get_ulr_resource(self) -> Resource: + """ + 获取UlrResource对象 + :return: UlrResource对象 + """ + return self.ulr_resource + + def get_ulr_resource_as_dict(self) -> Resource: + """ + 获取UlrResource对象 + :return: UlrResource对象 + """ + return convert_from_ros_msg(self.ulr_resource) + + def __str__(self): + return f"{self.id}" \ No newline at end of file diff --git a/unilabos/resources/graphio.py b/unilabos/resources/graphio.py index cca7a35..418a81f 100644 --- a/unilabos/resources/graphio.py +++ b/unilabos/resources/graphio.py @@ -5,6 +5,8 @@ from typing import Union import numpy as np import networkx as nx +from unilabos.resources.container import RegularContainer + try: from pylabrobot.resources.resource import Resource as ResourcePLR except ImportError: @@ -466,6 +468,9 @@ def initialize_resource(resource_config: dict) -> list[dict]: if resource_config.get("position") is not None: 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", {})) + r = [res_instance.get_ulr_resource_as_dict()] elif isinstance(RESOURCE, dict): r = [RESOURCE.copy()] diff --git a/unilabos/ros/nodes/base_device_node.py b/unilabos/ros/nodes/base_device_node.py index 5b93b72..d39972f 100644 --- a/unilabos/ros/nodes/base_device_node.py +++ b/unilabos/ros/nodes/base_device_node.py @@ -343,6 +343,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") if slot != "-1": # slot为负数的时候采用assign方法 other_calling_param["slot"] = slot diff --git a/unilabos/ros/nodes/presets/host_node.py b/unilabos/ros/nodes/presets/host_node.py index 712e9b3..fef9d64 100644 --- a/unilabos/ros/nodes/presets/host_node.py +++ b/unilabos/ros/nodes/presets/host_node.py @@ -383,18 +383,24 @@ class HostNode(BaseROS2DeviceNode): liquid_volume: list[int], slot_on_deck: str, ): - init_new_res = initialize_resource( - { - "name": res_id, - "class": class_name, - "parent": parent, - "position": { - "x": bind_locations.x, - "y": bind_locations.y, - "z": bind_locations.z, - }, - } - ) # flatten的格式 + res_creation_input = { + "name": res_id, + "class": class_name, + "parent": parent, + "position": { + "x": bind_locations.x, + "y": bind_locations.y, + "z": bind_locations.z, + }, + } + if len(liquid_input_slot) and liquid_input_slot[0] == -1: # 目前container只逐个创建 + res_creation_input.update({ + "data": { + "liquid_type": liquid_type[0] if liquid_type else None, + "liquid_volume": liquid_volume[0] if liquid_volume else None, + } + }) + init_new_res = initialize_resource(res_creation_input) # flatten的格式 resources = init_new_res # initialize_resource已经返回list[dict] device_ids = [device_id] bind_parent_id = [parent]