diff --git a/unilabos/devices/liquid_handling/prcxi/prcxi.py b/unilabos/devices/liquid_handling/prcxi/prcxi.py index 10313c70..94a831d3 100644 --- a/unilabos/devices/liquid_handling/prcxi/prcxi.py +++ b/unilabos/devices/liquid_handling/prcxi/prcxi.py @@ -67,7 +67,7 @@ class PRCXI9300Deck(Deck): class PRCXI9300Container(Plate, TipRack): - """PRCXI 9300 的专用 Deck 类,继承自 Deck。 + """PRCXI 9300 的专用 Container 类,继承自 Plate和TipRack。 该类定义了 PRCXI 9300 的工作台布局和槽位信息。 """ diff --git a/unilabos/registry/devices/work_station.yaml b/unilabos/registry/devices/work_station.yaml index 44805c6a..0ec9d079 100644 --- a/unilabos/registry/devices/work_station.yaml +++ b/unilabos/registry/devices/work_station.yaml @@ -6136,3 +6136,109 @@ workstation: required: [] type: object version: 1.0.0 +workstation.example: + category: + - work_station + class: + action_value_mappings: + auto-append_resource: + feedback: {} + goal: {} + goal_default: {} + handles: [] + result: {} + schema: + description: '' + properties: + feedback: {} + goal: + properties: {} + required: [] + type: object + result: {} + required: + - goal + title: append_resource参数 + type: object + type: UniLabJsonCommand + auto-create_resource: + feedback: {} + goal: {} + goal_default: + bind_location: null + bind_parent_id: null + liquid_input_slot: null + liquid_type: null + liquid_volume: null + resource_tracker: null + resources: null + slot_on_deck: null + handles: [] + result: {} + schema: + description: '' + properties: + feedback: {} + goal: + properties: + bind_location: + type: object + bind_parent_id: + type: string + liquid_input_slot: + type: array + liquid_type: + type: array + liquid_volume: + type: array + resource_tracker: + type: string + resources: + type: array + slot_on_deck: + type: integer + required: + - resource_tracker + - resources + - bind_parent_id + - bind_location + - liquid_input_slot + - liquid_type + - liquid_volume + - slot_on_deck + type: object + result: {} + required: + - goal + title: create_resource参数 + type: object + type: UniLabJsonCommand + module: unilabos.ros.nodes.presets.workstation:WorkStationExample + status_types: {} + type: ros2 + config_info: [] + description: '' + handles: [] + icon: '' + init_param_schema: + config: + properties: + children: + type: object + device_id: + type: string + protocol_type: + type: string + resource_tracker: + type: string + required: + - device_id + - children + - protocol_type + - resource_tracker + type: object + data: + properties: {} + required: [] + type: object + version: 1.0.0 diff --git a/unilabos/registry/resources/organic/workstation.yaml b/unilabos/registry/resources/organic/workstation.yaml new file mode 100644 index 00000000..440f06c7 --- /dev/null +++ b/unilabos/registry/resources/organic/workstation.yaml @@ -0,0 +1,12 @@ +get_workstation_plate_resource: + category: + - workstation + class: + module: unilabos.ros.nodes.presets.workstation:get_workstation_plate_resource + type: pylabrobot + description: workstation example resource + handles: [] + icon: '' + init_param_schema: {} + registry_type: resource + version: 1.0.0 diff --git a/unilabos/resources/plr_additional_res_reg.py b/unilabos/resources/plr_additional_res_reg.py new file mode 100644 index 00000000..52ad3536 --- /dev/null +++ b/unilabos/resources/plr_additional_res_reg.py @@ -0,0 +1,9 @@ + + +def register(): + # noinspection PyUnresolvedReferences + from unilabos.devices.liquid_handling.prcxi.prcxi import PRCXI9300Deck + # noinspection PyUnresolvedReferences + from unilabos.devices.liquid_handling.prcxi.prcxi import PRCXI9300Container + # noinspection PyUnresolvedReferences + from unilabos.ros.nodes.presets.workstation import WorkStationContainer diff --git a/unilabos/ros/nodes/base_device_node.py b/unilabos/ros/nodes/base_device_node.py index 70bb2ece..9f3d703d 100644 --- a/unilabos/ros/nodes/base_device_node.py +++ b/unilabos/ros/nodes/base_device_node.py @@ -31,6 +31,7 @@ from unilabos.resources.graphio import ( resource_plr_to_ulab, tree_to_list, ) +from unilabos.resources.plr_additional_res_reg import register from unilabos.ros.msgs.message_converter import ( convert_to_ros_msg, convert_from_ros_msg, @@ -941,17 +942,14 @@ class ROS2DeviceNode: if use_pylabrobot_creator: # 先对pylabrobot的子资源进行加载,不然subclass无法认出 # 在下方对于加载Deck等Resource要手动import - # noinspection PyUnresolvedReferences - from unilabos.devices.liquid_handling.prcxi.prcxi import PRCXI9300Deck - # noinspection PyUnresolvedReferences - from unilabos.devices.liquid_handling.prcxi.prcxi import PRCXI9300Container + register() self._driver_creator = PyLabRobotCreator( driver_class, children=children, resource_tracker=self.resource_tracker ) else: from unilabos.ros.nodes.presets.protocol_node import ROS2ProtocolNode - if self._driver_class is ROS2ProtocolNode: + if issubclass(self._driver_class, ROS2ProtocolNode): # 是ProtocolNode的子节点,就要调用ProtocolNodeCreator self._driver_creator = ProtocolNodeCreator(driver_class, children=children, resource_tracker=self.resource_tracker) else: self._driver_creator = DeviceClassCreator(driver_class, children=children, resource_tracker=self.resource_tracker) diff --git a/unilabos/ros/nodes/presets/workstation.py b/unilabos/ros/nodes/presets/workstation.py new file mode 100644 index 00000000..da6b2528 --- /dev/null +++ b/unilabos/ros/nodes/presets/workstation.py @@ -0,0 +1,84 @@ +import collections +from typing import Union, Dict, Any, Optional + +from unilabos_msgs.msg import Resource +from pylabrobot.resources import Resource as PLRResource, Plate, TipRack, Coordinate +from unilabos.ros.nodes.presets.protocol_node import ROS2ProtocolNode +from unilabos.ros.nodes.resource_tracker import DeviceNodeResourceTracker + +class WorkStationContainer(Plate, TipRack): + """ + WorkStation 专用 Container 类,继承自 Plate和TipRack + 注意这个物料必须通过plr_additional_res_reg.py注册到edge,才能正常序列化 + """ + + def __init__(self, name: str, size_x: float, size_y: float, size_z: float, category: str, ordering: collections.OrderedDict, model: Optional[str] = None,): + """ + 这里的初始化入参要和plr的保持一致 + """ + super().__init__(name, size_x, size_y, size_z, category=category, ordering=ordering, model=model) + self._unilabos_state = {} # 必须有此行,自己的类描述的是物料的 + + def load_state(self, state: Dict[str, Any]) -> None: + """从给定的状态加载工作台信息。""" + super().load_state(state) + self._unilabos_state = state + + def serialize_state(self) -> Dict[str, Dict[str, Any]]: + data = super().serialize_state() + data.update(self._unilabos_state) # Container自身的信息,云端物料将保存这一data,本地也通过这里的data进行读写,当前类用来表示这个物料的长宽高大小的属性,而data(state用来表示物料的内容,细节等) + return data + + +def get_workstation_plate_resource(name: str) -> PLRResource: # 要给定一个返回plr的方法 + """ + 用于获取一些模板,例如返回一个带有特定信息/子物料的 Plate,这里需要到注册表注册,例如unilabos/registry/resources/organic/workstation.yaml + 可以直接运行该函数或者利用注册表补全机制,来检查是否资源出错 + :param name: 资源名称 + :return: Resource对象 + """ + plate = WorkStationContainer(name, size_x=50, size_y=50, size_z=10, category="plate", ordering=collections.OrderedDict()) + tip_rack = WorkStationContainer("tip_rack_inside_plate", size_x=50, size_y=50, size_z=10, category="tip_rack", ordering=collections.OrderedDict()) + plate.assign_child_resource(tip_rack, Coordinate.zero()) + return plate + + +class WorkStationExample(ROS2ProtocolNode): + def __init__(self, + # 你可以在这里增加任意的参数,对应启动json填写相应的参数内容 + device_id: str, + children: dict, + protocol_type: Union[str, list[str]], + resource_tracker: DeviceNodeResourceTracker + ): + super().__init__(device_id, children, protocol_type, resource_tracker) + + def create_resource( + self, + resource_tracker: DeviceNodeResourceTracker, + resources: list[Resource], + bind_parent_id: str, + bind_location: dict[str, float], + liquid_input_slot: list[int], + liquid_type: list[str], + liquid_volume: list[int], + slot_on_deck: int, + ) -> Dict[str, Any]: + return { # edge侧返回给前端的创建物料的结果。云端调用初始化瓶子等。执行该函数时,物料已经上报给云端,一般不需要继承使用 + + } + + def transfer_bottle(self, tip_rack: PLRResource, base_plate: PLRResource): # 使用自定义物料的举例 + """ + 将tip_rack assign给base_plate,两个入参都得是PLRResource,unilabos会代替当前物料操作,自动刷新他们的父子关系等状态 + """ + pass + + def trigger_resource_update(self, from_plate: PLRResource, to_base_plate: PLRResource): + """ + 有些时候物料发生了子设备的迁移,一般对该设备的最大一级的物料进行操作,例如要将A物料搬移到B物料上,他们不共同拥有一个物料 + 该步骤操作结束后,会主动刷新from_plate的父物料,与to_base_plate的父物料(如没有则刷新自身) + + """ + to_base_plate.assign_child_resource(from_plate, Coordinate.zero()) + pass \ No newline at end of file