refactor: ProtocolNode→WorkstationNode

This commit is contained in:
Junhan Chang
2025-08-25 22:09:37 +08:00
parent ae3c1100ae
commit 5ec8a57a1f
19 changed files with 1089 additions and 365 deletions

View File

@@ -50,7 +50,7 @@ from unilabos_msgs.msg import Resource # type: ignore
from unilabos.ros.nodes.resource_tracker import DeviceNodeResourceTracker
from unilabos.ros.x.rclpyx import get_event_loop
from unilabos.ros.utils.driver_creator import ProtocolNodeCreator, PyLabRobotCreator, DeviceClassCreator
from unilabos.ros.utils.driver_creator import WorkstationNodeCreator, PyLabRobotCreator, DeviceClassCreator
from unilabos.utils.async_util import run_async_func
from unilabos.utils.log import info, debug, warning, error, critical, logger, trace
from unilabos.utils.type_check import get_type_class, TypeEncoder, serialize_result_info
@@ -340,14 +340,11 @@ class BaseROS2DeviceNode(Node, Generic[T]):
# 物料传输到对应的node节点
rclient = self.create_client(ResourceAdd, "/resources/add")
rclient.wait_for_service()
rclient2 = self.create_client(ResourceAdd, "/resources/add")
rclient2.wait_for_service()
request = ResourceAdd.Request()
request2 = ResourceAdd.Request()
command_json = json.loads(req.command)
namespace = command_json["namespace"]
bind_parent_id = command_json["bind_parent_id"]
edge_device_id = command_json["edge_device_id"]
location = command_json["bind_location"]
other_calling_param = command_json["other_calling_param"]
resources = command_json["resource"]
@@ -357,7 +354,7 @@ class BaseROS2DeviceNode(Node, Generic[T]):
LIQUID_VOLUME = other_calling_param.pop("LIQUID_VOLUME", [])
LIQUID_INPUT_SLOT = other_calling_param.pop("LIQUID_INPUT_SLOT", [])
slot = other_calling_param.pop("slot", "-1")
resource = None
parent = None
if slot != "-1": # slot为负数的时候采用assign方法
other_calling_param["slot"] = slot
# 本地拿到这个物料,可能需要先做初始化?
@@ -385,20 +382,20 @@ class BaseROS2DeviceNode(Node, Generic[T]):
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
parent = found_resources[0]
if isinstance(parent, Resource):
regular_container = RegularContainer(parent.id)
regular_container.ulr_resource = parent
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))
request.resources[0].name = resource["name"]
logger.info(f"更新物料{container_query_dict['name']}的数据{resource['data']} dict")
logger.info(f"更新物料{container_query_dict['name']}的数据{parent.data} ULR")
elif isinstance(parent, dict):
if "data" not in parent:
parent["data"] = {}
parent["data"].update(json.loads(container_instance.data))
request.resources[0].name = parent["name"]
logger.info(f"更新物料{container_query_dict['name']}的数据{parent['data']} dict")
else:
logger.info(f"更新物料{container_query_dict['name']}出现不支持的数据类型{type(resource)} {resource}")
logger.info(f"更新物料{container_query_dict['name']}出现不支持的数据类型{type(parent)} {parent}")
response = await rclient.call_async(request)
# 应该先add_resource了
res.response = "OK"
@@ -423,18 +420,16 @@ class BaseROS2DeviceNode(Node, Generic[T]):
return res
# 接下来该根据bind_parent_id进行assign了目前只有plr可以进行assign不然没有办法输入到物料系统中
if bind_parent_id != self.node_name:
resource = self.resource_tracker.figure_resource({"name": bind_parent_id}) # 拿到父节点进行具体assign等操作
parent = self.resource_tracker.figure_resource({"name": bind_parent_id}) # 拿到父节点进行具体assign等操作
# request.resources = [convert_to_ros_msg(Resource, resources)]
try:
from pylabrobot.resources.resource import Resource as ResourcePLR
from pylabrobot.resources.deck import Deck
from pylabrobot.resources import Coordinate
from pylabrobot.resources import OTDeck
from pylabrobot.resources import Plate
from pylabrobot.resources import Coordinate, OTDeck, Plate
contain_model = not isinstance(resource, Deck)
if isinstance(resource, ResourcePLR):
contain_model = not isinstance(parent, Deck)
if isinstance(parent, ResourcePLR):
# resources.list()
resources_tree = dict_to_tree(copy.deepcopy({r["id"]: r for r in resources}))
plr_instance = resource_ulab_to_plr(resources_tree[0], contain_model)
@@ -445,20 +440,22 @@ class BaseROS2DeviceNode(Node, Generic[T]):
):
empty_liquid_info_in[liquid_input_slot] = (liquid_type, liquid_volume)
plr_instance.set_well_liquids(empty_liquid_info_in)
if isinstance(resource, OTDeck) and "slot" in other_calling_param:
if isinstance(parent, OTDeck) and "slot" in other_calling_param:
other_calling_param["slot"] = int(other_calling_param["slot"])
resource.assign_child_at_slot(plr_instance, **other_calling_param)
parent.assign_child_at_slot(plr_instance, **other_calling_param)
else:
_discard_slot = other_calling_param.pop("slot", "-1")
resource.assign_child_resource(
parent.assign_child_resource(
plr_instance,
Coordinate(location["x"], location["y"], location["z"]),
**other_calling_param,
)
request2 = ResourceAdd.Request()
request2.resources = [
convert_to_ros_msg(Resource, r) for r in tree_to_list([resource_plr_to_ulab(resource)])
convert_to_ros_msg(Resource, r) for r in tree_to_list([resource_plr_to_ulab(parent)])
]
rclient2.call(request2)
rclient.call(request2)
# 发送给ResourceMeshManager
action_client = ActionClient(
self,
@@ -947,7 +944,7 @@ class ROS2DeviceNode:
# TODO: 要在创建之前预先请求服务器是否有当前id的物料放到resource_tracker中让pylabrobot进行创建
# 创建设备类实例
# 判断是否包含设备子节点决定是否使用ROS2ProtocolNode
# 判断是否包含设备子节点决定是否使用ROS2WorkstationNode
has_device_children = any(
child_config.get("type", "device") == "device"
for child_config in children.values()
@@ -961,17 +958,17 @@ class ROS2DeviceNode:
driver_class, children=children, resource_tracker=self.resource_tracker
)
else:
from unilabos.ros.nodes.presets.protocol_node import ROS2ProtocolNode
from unilabos.device_comms.workstation_base import WorkstationBase
from unilabos.ros.nodes.presets.protocol_node import ROS2WorkstationNode
from unilabos.devices.work_station.workstation_base import WorkstationBase
# 检查是否是WorkstationBase的子类且包含设备子节点
if issubclass(self._driver_class, WorkstationBase) and has_device_children:
# WorkstationBase + 设备子节点 -> 使用ProtocolNode作为ros_instance
# WorkstationBase + 设备子节点 -> 使用WorkstationNode作为ros_instance
self._use_protocol_node_ros = True
self._driver_creator = DeviceClassCreator(driver_class, children=children, resource_tracker=self.resource_tracker)
elif issubclass(self._driver_class, ROS2ProtocolNode): # 是ProtocolNode的子节点就要调用ProtocolNodeCreator
elif issubclass(self._driver_class, ROS2WorkstationNode): # 是WorkstationNode的子节点就要调用WorkstationNodeCreator
self._use_protocol_node_ros = False
self._driver_creator = ProtocolNodeCreator(driver_class, children=children, resource_tracker=self.resource_tracker)
self._driver_creator = WorkstationNodeCreator(driver_class, children=children, resource_tracker=self.resource_tracker)
else:
self._use_protocol_node_ros = False
self._driver_creator = DeviceClassCreator(driver_class, children=children, resource_tracker=self.resource_tracker)
@@ -988,8 +985,8 @@ class ROS2DeviceNode:
if driver_is_ros:
self._ros_node = self._driver_instance # type: ignore
elif hasattr(self, '_use_protocol_node_ros') and self._use_protocol_node_ros:
# WorkstationBase + 设备子节点 -> 创建ROS2ProtocolNode作为ros_instance
from unilabos.ros.nodes.presets.protocol_node import ROS2ProtocolNode
# WorkstationBase + 设备子节点 -> 创建ROS2WorkstationNode作为ros_instance
from unilabos.ros.nodes.presets.protocol_node import ROS2WorkstationNode
# 从children提取设备协议类型
protocol_types = set()
@@ -1006,7 +1003,7 @@ class ROS2DeviceNode:
if not protocol_types:
protocol_types = ["default_protocol"]
self._ros_node = ROS2ProtocolNode(
self._ros_node = ROS2WorkstationNode(
device_id=device_id,
children=children,
protocol_type=list(protocol_types),