mirror of
https://github.com/dptech-corp/Uni-Lab-OS.git
synced 2025-12-17 13:01:12 +00:00
fix protocol node log_message, added create_resource return value
This commit is contained in:
@@ -1093,7 +1093,7 @@ class WebSocketClient(BaseCommunicationClient):
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
self.message_processor.send_message(message)
|
self.message_processor.send_message(message)
|
||||||
logger.debug(f"[WebSocketClient] Device status published: {device_id}.{property_name}")
|
logger.trace(f"[WebSocketClient] Device status published: {device_id}.{property_name}")
|
||||||
|
|
||||||
def publish_job_status(
|
def publish_job_status(
|
||||||
self, feedback_data: dict, item: QueueItem, status: str, return_info: Optional[dict] = None
|
self, feedback_data: dict, item: QueueItem, status: str, return_info: Optional[dict] = None
|
||||||
|
|||||||
@@ -294,7 +294,7 @@ class Registry:
|
|||||||
logger.warning(f"[UniLab Registry] 设备 {device_id} 的 {field_name} 类型为空,跳过替换")
|
logger.warning(f"[UniLab Registry] 设备 {device_id} 的 {field_name} 类型为空,跳过替换")
|
||||||
return type_name
|
return type_name
|
||||||
convert_manager = { # 将python基本对象转为ros2基本对象
|
convert_manager = { # 将python基本对象转为ros2基本对象
|
||||||
"builtins:str": "String",
|
"str": "String",
|
||||||
"bool": "Bool",
|
"bool": "Bool",
|
||||||
"int": "Int64",
|
"int": "Int64",
|
||||||
"float": "Float64",
|
"float": "Float64",
|
||||||
|
|||||||
@@ -430,7 +430,7 @@ def resource_ulab_to_plr(resource: dict, plr_model=False) -> "ResourcePLR":
|
|||||||
return resource_plr
|
return resource_plr
|
||||||
|
|
||||||
|
|
||||||
def resource_plr_to_ulab(resource_plr: "ResourcePLR", parent_name: str = None):
|
def resource_plr_to_ulab(resource_plr: "ResourcePLR", parent_name: str = None, with_children=True):
|
||||||
def replace_plr_type_to_ulab(source: str):
|
def replace_plr_type_to_ulab(source: str):
|
||||||
replace_info = {
|
replace_info = {
|
||||||
"plate": "plate",
|
"plate": "plate",
|
||||||
@@ -445,12 +445,12 @@ def resource_plr_to_ulab(resource_plr: "ResourcePLR", parent_name: str = None):
|
|||||||
else:
|
else:
|
||||||
print("转换pylabrobot的时候,出现未知类型", source)
|
print("转换pylabrobot的时候,出现未知类型", source)
|
||||||
return "container"
|
return "container"
|
||||||
def resource_plr_to_ulab_inner(d: dict, all_states: dict) -> dict:
|
def resource_plr_to_ulab_inner(d: dict, all_states: dict, child=True) -> dict:
|
||||||
r = {
|
r = {
|
||||||
"id": d["name"],
|
"id": d["name"],
|
||||||
"name": d["name"],
|
"name": d["name"],
|
||||||
"sample_id": None,
|
"sample_id": None,
|
||||||
"children": [resource_plr_to_ulab_inner(child, all_states) for child in d["children"]],
|
"children": [resource_plr_to_ulab_inner(child, all_states) for child in d["children"]] if child else [],
|
||||||
"parent": d["parent_name"] if d["parent_name"] else parent_name if parent_name else None,
|
"parent": d["parent_name"] if d["parent_name"] else parent_name if parent_name else None,
|
||||||
"type": replace_plr_type_to_ulab(d.get("category")), # FIXME plr自带的type是python class name
|
"type": replace_plr_type_to_ulab(d.get("category")), # FIXME plr自带的type是python class name
|
||||||
"class": d.get("class", ""),
|
"class": d.get("class", ""),
|
||||||
@@ -465,7 +465,7 @@ def resource_plr_to_ulab(resource_plr: "ResourcePLR", parent_name: str = None):
|
|||||||
return r
|
return r
|
||||||
d = resource_plr.serialize()
|
d = resource_plr.serialize()
|
||||||
all_states = resource_plr.serialize_all_state()
|
all_states = resource_plr.serialize_all_state()
|
||||||
r = resource_plr_to_ulab_inner(d, all_states)
|
r = resource_plr_to_ulab_inner(d, all_states, with_children)
|
||||||
|
|
||||||
return r
|
return r
|
||||||
|
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import asyncio
|
|||||||
|
|
||||||
import rclpy
|
import rclpy
|
||||||
import yaml
|
import yaml
|
||||||
|
from msgcenterpy import ROS2MessageInstance
|
||||||
from rclpy.node import Node
|
from rclpy.node import Node
|
||||||
from rclpy.action import ActionServer, ActionClient
|
from rclpy.action import ActionServer, ActionClient
|
||||||
from rclpy.action.server import ServerGoalHandle
|
from rclpy.action.server import ServerGoalHandle
|
||||||
@@ -177,7 +178,7 @@ class PropertyPublisher:
|
|||||||
try:
|
try:
|
||||||
self.publisher_ = node.create_publisher(msg_type, f"{name}", 10)
|
self.publisher_ = node.create_publisher(msg_type, f"{name}", 10)
|
||||||
except AttributeError as ex:
|
except AttributeError as ex:
|
||||||
logger.error(f"创建发布者失败,可能由于注册表有误,类型: {msg_type},错误: {ex}\n{traceback.format_exc()}")
|
self.node.lab_logger().error(f"创建发布者 {name} 失败,可能由于注册表有误,类型: {msg_type},错误: {ex}\n{traceback.format_exc()}")
|
||||||
self.timer = node.create_timer(self.timer_period, self.publish_property)
|
self.timer = node.create_timer(self.timer_period, self.publish_property)
|
||||||
self.__loop = get_event_loop()
|
self.__loop = get_event_loop()
|
||||||
str_msg_type = str(msg_type)[8:-2]
|
str_msg_type = str(msg_type)[8:-2]
|
||||||
@@ -399,9 +400,13 @@ class BaseROS2DeviceNode(Node, Generic[T]):
|
|||||||
logger.info(f"更新物料{container_query_dict['name']}的数据{resource['data']} dict")
|
logger.info(f"更新物料{container_query_dict['name']}的数据{resource['data']} dict")
|
||||||
else:
|
else:
|
||||||
logger.info(f"更新物料{container_query_dict['name']}出现不支持的数据类型{type(resource)} {resource}")
|
logger.info(f"更新物料{container_query_dict['name']}出现不支持的数据类型{type(resource)} {resource}")
|
||||||
response = await rclient.call_async(request)
|
response: ResourceAdd.Response = await rclient.call_async(request)
|
||||||
# 应该先add_resource了
|
# 应该先add_resource了
|
||||||
res.response = "OK"
|
final_response = {
|
||||||
|
"created_resources": [ROS2MessageInstance(i).get_python_dict() for i in request.resources],
|
||||||
|
"liquid_input_resources": []
|
||||||
|
}
|
||||||
|
res.response = json.dumps(final_response)
|
||||||
# 如果driver自己就有assign的方法,那就使用driver自己的assign方法
|
# 如果driver自己就有assign的方法,那就使用driver自己的assign方法
|
||||||
if hasattr(self.driver_instance, "create_resource"):
|
if hasattr(self.driver_instance, "create_resource"):
|
||||||
create_resource_func = getattr(self.driver_instance, "create_resource")
|
create_resource_func = getattr(self.driver_instance, "create_resource")
|
||||||
@@ -418,7 +423,7 @@ class BaseROS2DeviceNode(Node, Generic[T]):
|
|||||||
)
|
)
|
||||||
res.response = get_result_info_str("", True, ret)
|
res.response = get_result_info_str("", True, ret)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
traceback.print_exc()
|
self.lab_logger().error(f"运行设备的create_resource出错:{create_resource_func}\n{traceback.format_exc()}")
|
||||||
res.response = get_result_info_str(traceback.format_exc(), False, {})
|
res.response = get_result_info_str(traceback.format_exc(), False, {})
|
||||||
return res
|
return res
|
||||||
# 接下来该根据bind_parent_id进行assign了,目前只有plr可以进行assign,不然没有办法输入到物料系统中
|
# 接下来该根据bind_parent_id进行assign了,目前只有plr可以进行assign,不然没有办法输入到物料系统中
|
||||||
@@ -438,6 +443,7 @@ class BaseROS2DeviceNode(Node, Generic[T]):
|
|||||||
# resources.list()
|
# resources.list()
|
||||||
resources_tree = dict_to_tree(copy.deepcopy({r["id"]: r for r in resources}))
|
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)
|
plr_instance = resource_ulab_to_plr(resources_tree[0], contain_model)
|
||||||
|
|
||||||
if isinstance(plr_instance, Plate):
|
if isinstance(plr_instance, Plate):
|
||||||
empty_liquid_info_in = [(None, 0)] * plr_instance.num_items
|
empty_liquid_info_in = [(None, 0)] * plr_instance.num_items
|
||||||
for liquid_type, liquid_volume, liquid_input_slot in zip(
|
for liquid_type, liquid_volume, liquid_input_slot in zip(
|
||||||
@@ -445,6 +451,11 @@ class BaseROS2DeviceNode(Node, Generic[T]):
|
|||||||
):
|
):
|
||||||
empty_liquid_info_in[liquid_input_slot] = (liquid_type, liquid_volume)
|
empty_liquid_info_in[liquid_input_slot] = (liquid_type, liquid_volume)
|
||||||
plr_instance.set_well_liquids(empty_liquid_info_in)
|
plr_instance.set_well_liquids(empty_liquid_info_in)
|
||||||
|
input_wells_ulr = [
|
||||||
|
convert_to_ros_msg(Resource, resource_plr_to_ulab(plr_instance.get_well(LIQUID_INPUT_SLOT), with_children=False)) for r in LIQUID_INPUT_SLOT
|
||||||
|
]
|
||||||
|
final_response["liquid_input_resources"] = [ROS2MessageInstance(i).get_python_dict() for i in input_wells_ulr]
|
||||||
|
res.response = json.dumps(final_response)
|
||||||
if isinstance(resource, OTDeck) and "slot" in other_calling_param:
|
if isinstance(resource, OTDeck) and "slot" in other_calling_param:
|
||||||
other_calling_param["slot"] = int(other_calling_param["slot"])
|
other_calling_param["slot"] = int(other_calling_param["slot"])
|
||||||
resource.assign_child_at_slot(plr_instance, **other_calling_param)
|
resource.assign_child_at_slot(plr_instance, **other_calling_param)
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ from geometry_msgs.msg import Point
|
|||||||
from rclpy.action import ActionClient, get_action_server_names_and_types_by_node
|
from rclpy.action import ActionClient, get_action_server_names_and_types_by_node
|
||||||
from rclpy.callback_groups import ReentrantCallbackGroup
|
from rclpy.callback_groups import ReentrantCallbackGroup
|
||||||
from rclpy.service import Service
|
from rclpy.service import Service
|
||||||
|
from rosidl_runtime_py import set_message_fields
|
||||||
from unilabos_msgs.msg import Resource # type: ignore
|
from unilabos_msgs.msg import Resource # type: ignore
|
||||||
from unilabos_msgs.srv import (
|
from unilabos_msgs.srv import (
|
||||||
ResourceAdd,
|
ResourceAdd,
|
||||||
@@ -366,7 +367,7 @@ class HostNode(BaseROS2DeviceNode):
|
|||||||
bind_parent_ids: list[str],
|
bind_parent_ids: list[str],
|
||||||
bind_locations: list[Point],
|
bind_locations: list[Point],
|
||||||
other_calling_params: list[str],
|
other_calling_params: list[str],
|
||||||
):
|
) -> List[str]:
|
||||||
responses = []
|
responses = []
|
||||||
for resource, device_id, bind_parent_id, bind_location, other_calling_param in zip(
|
for resource, device_id, bind_parent_id, bind_location, other_calling_param in zip(
|
||||||
resources, device_ids, bind_parent_ids, bind_locations, other_calling_params
|
resources, device_ids, bind_parent_ids, bind_locations, other_calling_params
|
||||||
@@ -403,8 +404,8 @@ class HostNode(BaseROS2DeviceNode):
|
|||||||
},
|
},
|
||||||
ensure_ascii=False,
|
ensure_ascii=False,
|
||||||
)
|
)
|
||||||
response = await sclient.call_async(request)
|
response: SerialCommand.Response = await sclient.call_async(request)
|
||||||
responses.append(response)
|
responses.append(response.response)
|
||||||
return responses
|
return responses
|
||||||
|
|
||||||
async def create_resource(
|
async def create_resource(
|
||||||
@@ -463,11 +464,23 @@ class HostNode(BaseROS2DeviceNode):
|
|||||||
)
|
)
|
||||||
]
|
]
|
||||||
|
|
||||||
response = await self.create_resource_detailed(
|
response: List[str] = await self.create_resource_detailed(
|
||||||
resources, device_ids, bind_parent_id, bind_location, other_calling_param
|
resources, device_ids, bind_parent_id, bind_location, other_calling_param
|
||||||
)
|
)
|
||||||
|
|
||||||
return response
|
try:
|
||||||
|
new_li = []
|
||||||
|
for i in response:
|
||||||
|
res = json.loads(i)
|
||||||
|
new_li.append(res)
|
||||||
|
return {
|
||||||
|
"resources": new_li,
|
||||||
|
"liquid_input_resources": new_li
|
||||||
|
}
|
||||||
|
except Exception as ex:
|
||||||
|
pass
|
||||||
|
_n = "\n"
|
||||||
|
raise ValueError(f"创建资源时失败!\n{_n.join(response)}")
|
||||||
|
|
||||||
def initialize_device(self, device_id: str, device_config: Dict[str, Any]) -> None:
|
def initialize_device(self, device_id: str, device_config: Dict[str, Any]) -> None:
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -218,7 +218,7 @@ class ROS2ProtocolNode(BaseROS2DeviceNode):
|
|||||||
[convert_from_ros_msg(rs) for rs in response.resources]
|
[convert_from_ros_msg(rs) for rs in response.resources]
|
||||||
)
|
)
|
||||||
|
|
||||||
self.lab_logger().info(f"🔍 最终的 vessel: {protocol_kwargs.get('vessel', 'NOT_FOUND')}")
|
# self.lab_logger().info(f"🔍 最终的 vessel: {protocol_kwargs.get('vessel', 'NOT_FOUND')}")
|
||||||
|
|
||||||
from unilabos.resources.graphio import physical_setup_graph
|
from unilabos.resources.graphio import physical_setup_graph
|
||||||
|
|
||||||
@@ -325,6 +325,8 @@ class ROS2ProtocolNode(BaseROS2DeviceNode):
|
|||||||
async def execute_single_action(self, device_id, action_name, action_kwargs):
|
async def execute_single_action(self, device_id, action_name, action_kwargs):
|
||||||
"""执行单个动作"""
|
"""执行单个动作"""
|
||||||
# 构建动作ID
|
# 构建动作ID
|
||||||
|
if action_name == "log_message":
|
||||||
|
self.lab_logger().info(f"[Protocol Log] {action_kwargs}")
|
||||||
if device_id in ["", None, "self"]:
|
if device_id in ["", None, "self"]:
|
||||||
action_id = f"/devices/{self.device_id}/{action_name}"
|
action_id = f"/devices/{self.device_id}/{action_name}"
|
||||||
else:
|
else:
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ class EnvironmentChecker:
|
|||||||
# 包导入名 : pip安装名
|
# 包导入名 : pip安装名
|
||||||
# "pymodbus.framer.FramerType": "pymodbus==3.9.2",
|
# "pymodbus.framer.FramerType": "pymodbus==3.9.2",
|
||||||
"websockets": "websockets",
|
"websockets": "websockets",
|
||||||
"paho.mqtt": "paho-mqtt",
|
"msgcenterpy": "msgcenterpy",
|
||||||
"opentrons_shared_data": "opentrons_shared_data",
|
"opentrons_shared_data": "opentrons_shared_data",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user