修改sample_uuid的返回值

This commit is contained in:
zhangshixiang
2025-12-06 01:33:04 +08:00
parent b1cdef9185
commit 91aadba4ef
9 changed files with 44334 additions and 32 deletions

View File

@@ -6,6 +6,7 @@ import traceback
from collections import Counter from collections import Counter
from typing import List, Sequence, Optional, Literal, Union, Iterator, Dict, Any, Callable, Set, cast from typing import List, Sequence, Optional, Literal, Union, Iterator, Dict, Any, Callable, Set, cast
from typing_extensions import TypedDict
from pylabrobot.liquid_handling import LiquidHandler, LiquidHandlerBackend, LiquidHandlerChatterboxBackend, Strictness from pylabrobot.liquid_handling import LiquidHandler, LiquidHandlerBackend, LiquidHandlerChatterboxBackend, Strictness
from unilabos.devices.liquid_handling.rviz_backend import UniLiquidHandlerRvizBackend from unilabos.devices.liquid_handling.rviz_backend import UniLiquidHandlerRvizBackend
from unilabos.devices.liquid_handling.laiyu.backend.laiyu_v_backend import UniLiquidHandlerLaiyuBackend from unilabos.devices.liquid_handling.laiyu.backend.laiyu_v_backend import UniLiquidHandlerLaiyuBackend
@@ -28,7 +29,9 @@ from pylabrobot.resources import (
) )
from unilabos.ros.nodes.base_device_node import BaseROS2DeviceNode from unilabos.ros.nodes.base_device_node import BaseROS2DeviceNode
class SimpleReturn(TypedDict):
samples: list
volumes: list
class LiquidHandlerMiddleware(LiquidHandler): class LiquidHandlerMiddleware(LiquidHandler):
def __init__(self, backend: LiquidHandlerBackend, deck: Deck, simulator: bool = False, channel_num: int = 8, **kwargs): def __init__(self, backend: LiquidHandlerBackend, deck: Deck, simulator: bool = False, channel_num: int = 8, **kwargs):
@@ -564,10 +567,16 @@ class LiquidHandlerAbstract(LiquidHandlerMiddleware):
self._ros_node = ros_node self._ros_node = ros_node
@classmethod @classmethod
def set_liquid(cls, wells: list[Well], liquid_names: list[str], volumes: list[float]): def set_liquid(cls, wells: list[Well], liquid_names: list[str], volumes: list[float]) -> SimpleReturn:
"""Set the liquid in a well.""" """Set the liquid in a well."""
res_samples = []
res_volumes = []
for well, liquid_name, volume in zip(wells, liquid_names, volumes): for well, liquid_name, volume in zip(wells, liquid_names, volumes):
well.set_liquids([(liquid_name, volume)]) # type: ignore well.set_liquids([(liquid_name, volume)]) # type: ignore
res_samples.append({"name": well.name, "sample_uuid": well.unilabos_extra.get("sample_uuid", None)})
res_volumes.append(volume)
return SimpleReturn(samples=res_samples, volumes=res_volumes)
# --------------------------------------------------------------- # ---------------------------------------------------------------
# REMOVE LIQUID -------------------------------------------------- # REMOVE LIQUID --------------------------------------------------
# --------------------------------------------------------------- # ---------------------------------------------------------------

View File

@@ -29,7 +29,7 @@ from pylabrobot.liquid_handling.standard import (
) )
from pylabrobot.resources import Tip, Deck, Plate, Well, TipRack, Resource, Container, Coordinate, TipSpot, Trash from pylabrobot.resources import Tip, Deck, Plate, Well, TipRack, Resource, Container, Coordinate, TipSpot, Trash
from unilabos.devices.liquid_handling.liquid_handler_abstract import LiquidHandlerAbstract from unilabos.devices.liquid_handling.liquid_handler_abstract import LiquidHandlerAbstract, SimpleReturn
from unilabos.ros.nodes.base_device_node import BaseROS2DeviceNode from unilabos.ros.nodes.base_device_node import BaseROS2DeviceNode
@@ -176,7 +176,7 @@ class PRCXI9300Handler(LiquidHandlerAbstract):
super().post_init(ros_node) super().post_init(ros_node)
self._unilabos_backend.post_init(ros_node) self._unilabos_backend.post_init(ros_node)
def set_liquid(self, wells: list[Well], liquid_names: list[str], volumes: list[float]): def set_liquid(self, wells: list[Well], liquid_names: list[str], volumes: list[float]) -> SimpleReturn:
return super().set_liquid(wells, liquid_names, volumes) return super().set_liquid(wells, liquid_names, volumes)
def set_group(self, group_name: str, wells: List[Well], volumes: List[float]): def set_group(self, group_name: str, wells: List[Well], volumes: List[float]):
@@ -505,7 +505,11 @@ class PRCXI9300Backend(LiquidHandlerBackend):
print(f"PRCXI9300Backend created solution with ID: {solution_id}") print(f"PRCXI9300Backend created solution with ID: {solution_id}")
self.api_client.load_solution(solution_id) self.api_client.load_solution(solution_id)
print(json.dumps(self.steps_todo_list, indent=2)) print(json.dumps(self.steps_todo_list, indent=2))
return self.api_client.start() if not self.api_client.start():
return False
if not self.api_client.wait_for_finish():
return False
return True
@classmethod @classmethod
def check_channels(cls, use_channels: List[int]) -> List[int]: def check_channels(cls, use_channels: List[int]) -> List[int]:
@@ -890,6 +894,26 @@ class PRCXI9300Api:
def start(self) -> bool: def start(self) -> bool:
return self.call("IAutomation", "Start") return self.call("IAutomation", "Start")
def wait_for_finish(self) -> bool:
success = False
start = False
while not success:
status = self.step_state_list()
if status is None:
break
if len(status) == 0:
break
if status[-1]["State"] == 2 and start:
success = True
elif status[-1]["State"] > 2:
break
elif status[-1]["State"] == 0:
start = True
else:
time.sleep(1)
return success
def call(self, service: str, method: str, params: Optional[list] = None) -> Any: def call(self, service: str, method: str, params: Optional[list] = None) -> Any:
payload = json.dumps( payload = json.dumps(
{"ServiceName": service, "MethodName": method, "Paramters": params or []}, separators=(",", ":") {"ServiceName": service, "MethodName": method, "Paramters": params or []}, separators=(",", ":")

View File

@@ -1361,7 +1361,8 @@ laiyu_liquid:
mix_liquid_height: 0.0 mix_liquid_height: 0.0
mix_rate: 0 mix_rate: 0
mix_stage: '' mix_stage: ''
mix_times: 0 mix_times:
- 0
mix_vol: 0 mix_vol: 0
none_keys: none_keys:
- '' - ''
@@ -1491,9 +1492,11 @@ laiyu_liquid:
mix_stage: mix_stage:
type: string type: string
mix_times: mix_times:
items:
maximum: 2147483647 maximum: 2147483647
minimum: -2147483648 minimum: -2147483648
type: integer type: integer
type: array
mix_vol: mix_vol:
maximum: 2147483647 maximum: 2147483647
minimum: -2147483648 minimum: -2147483648

View File

@@ -4019,7 +4019,8 @@ liquid_handler:
mix_liquid_height: 0.0 mix_liquid_height: 0.0
mix_rate: 0 mix_rate: 0
mix_stage: '' mix_stage: ''
mix_times: 0 mix_times:
- 0
mix_vol: 0 mix_vol: 0
none_keys: none_keys:
- '' - ''
@@ -4175,9 +4176,11 @@ liquid_handler:
mix_stage: mix_stage:
type: string type: string
mix_times: mix_times:
items:
maximum: 2147483647 maximum: 2147483647
minimum: -2147483648 minimum: -2147483648
type: integer type: integer
type: array
mix_vol: mix_vol:
maximum: 2147483647 maximum: 2147483647
minimum: -2147483648 minimum: -2147483648
@@ -5037,7 +5040,8 @@ liquid_handler.biomek:
mix_liquid_height: 0.0 mix_liquid_height: 0.0
mix_rate: 0 mix_rate: 0
mix_stage: '' mix_stage: ''
mix_times: 0 mix_times:
- 0
mix_vol: 0 mix_vol: 0
none_keys: none_keys:
- '' - ''
@@ -5180,9 +5184,11 @@ liquid_handler.biomek:
mix_stage: mix_stage:
type: string type: string
mix_times: mix_times:
items:
maximum: 2147483647 maximum: 2147483647
minimum: -2147483648 minimum: -2147483648
type: integer type: integer
type: array
mix_vol: mix_vol:
maximum: 2147483647 maximum: 2147483647
minimum: -2147483648 minimum: -2147483648
@@ -9261,7 +9267,8 @@ liquid_handler.prcxi:
mix_liquid_height: 0.0 mix_liquid_height: 0.0
mix_rate: 0 mix_rate: 0
mix_stage: '' mix_stage: ''
mix_times: 0 mix_times:
- 0
mix_vol: 0 mix_vol: 0
none_keys: none_keys:
- '' - ''
@@ -9390,9 +9397,11 @@ liquid_handler.prcxi:
mix_stage: mix_stage:
type: string type: string
mix_times: mix_times:
items:
maximum: 2147483647 maximum: 2147483647
minimum: -2147483648 minimum: -2147483648
type: integer type: integer
type: array
mix_vol: mix_vol:
maximum: 2147483647 maximum: 2147483647
minimum: -2147483648 minimum: -2147483648

View File

@@ -1142,20 +1142,28 @@ class BaseROS2DeviceNode(Node, Generic[T]):
plr_resource = await self.get_resource_with_dir( plr_resource = await self.get_resource_with_dir(
resource_id=resource_data["id"], with_children=True resource_id=resource_data["id"], with_children=True
) )
if "sample_id" in resource_data:
plr_resource.unilabos_extra["sample_uuid"] = resource_data["sample_id"]
queried_resources.append(plr_resource) queried_resources.append(plr_resource)
self.lab_logger().debug(f"资源查询结果: 共 {len(queried_resources)} 个资源") self.lab_logger().debug(f"资源查询结果: 共 {len(queried_resources)} 个资源")
# 通过资源跟踪器获取本地实例 # 通过资源跟踪器获取本地实例
final_resources = queried_resources if is_sequence else queried_resources[0] final_resources = queried_resources if is_sequence else queried_resources[0]
final_resources = ( if not is_sequence:
self.resource_tracker.figure_resource({"name": final_resources.name}, try_mode=False) plr = self.resource_tracker.figure_resource({"name": final_resources.name}, try_mode=False)
if not is_sequence # 保留unilabos_extra
else [ if hasattr(final_resources, "unilabos_extra") and hasattr(plr, "unilabos_extra"):
self.resource_tracker.figure_resource({"name": res.name}, try_mode=False) plr.unilabos_extra = getattr(final_resources, "unilabos_extra", {}).copy()
for res in queried_resources final_resources = plr
] else:
) new_resources = []
for res in queried_resources:
plr = self.resource_tracker.figure_resource({"name": res.name}, try_mode=False)
if hasattr(res, "unilabos_extra") and hasattr(plr, "unilabos_extra"):
plr.unilabos_extra = getattr(res, "unilabos_extra", {}).copy()
new_resources.append(plr)
final_resources = new_resources
action_kwargs[k] = final_resources action_kwargs[k] = final_resources
except Exception as e: except Exception as e:

View File

@@ -706,7 +706,20 @@ class HostNode(BaseROS2DeviceNode):
raise ValueError(f"ActionClient {action_id} not found.") raise ValueError(f"ActionClient {action_id} not found.")
action_client: ActionClient = self._action_clients[action_id] action_client: ActionClient = self._action_clients[action_id]
# 遍历action_kwargs下的所有子dict将"sample_uuid"的值赋给"sample_id"
def assign_sample_id(obj):
if isinstance(obj, dict):
if "sample_uuid" in obj:
obj["sample_id"] = obj["sample_uuid"]
obj.pop("sample_uuid")
for k,v in obj.items():
if k != "unilabos_extra":
assign_sample_id(v)
elif isinstance(obj, list):
for item in obj:
assign_sample_id(item)
assign_sample_id(action_kwargs)
goal_msg = convert_to_ros_msg(action_client._action_type.Goal(), action_kwargs) goal_msg = convert_to_ros_msg(action_client._action_type.Goal(), action_kwargs)
self.lab_logger().info(f"[Host Node] Sending goal for {action_id}: {goal_msg}") self.lab_logger().info(f"[Host Node] Sending goal for {action_id}: {goal_msg}")

View File

@@ -146,8 +146,20 @@ class ResourceDictInstance(object):
content["data"] = {} content["data"] = {}
if not content.get("extra"): # MagicCode if not content.get("extra"): # MagicCode
content["extra"] = {} content["extra"] = {}
if "pose" not in content: if "position" in content:
content["pose"] = content.pop("position", {}) pose = content["config"].get("pose",{})
if "position" not in pose :
if "position" in content["position"]:
pose["position"] = content["position"]["position"]
else:
pose["position"] = {"x": 0, "y": 0, "z": 0}
if "size" not in pose:
pose["size"] = {
"width": content["config"].get("size_x", 0),
"height": content["config"].get("size_y", 0),
"depth": content["config"].get("size_z", 0)
}
content["pose"] = pose
return ResourceDictInstance(ResourceDict.model_validate(content)) return ResourceDictInstance(ResourceDict.model_validate(content))
def get_plr_nested_dict(self) -> Dict[str, Any]: def get_plr_nested_dict(self) -> Dict[str, Any]:

File diff suppressed because it is too large Load Diff

View File

@@ -78,7 +78,11 @@ def get_result_info_str(error: str, suc: bool, return_value=None) -> str:
Returns: Returns:
JSON字符串格式的结果信息 JSON字符串格式的结果信息
""" """
result_info = {"error": error, "suc": suc, "return_value": return_value} samples = None
if isinstance(return_value, dict):
if "samples" in return_value:
samples = return_value.pop("samples")
result_info = {"error": error, "suc": suc, "return_value": return_value, "samples": samples}
return json.dumps(result_info, ensure_ascii=False, cls=ResultInfoEncoder) return json.dumps(result_info, ensure_ascii=False, cls=ResultInfoEncoder)