修改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 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 unilabos.devices.liquid_handling.rviz_backend import UniLiquidHandlerRvizBackend
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
class SimpleReturn(TypedDict):
samples: list
volumes: list
class LiquidHandlerMiddleware(LiquidHandler):
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
@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."""
res_samples = []
res_volumes = []
for well, liquid_name, volume in zip(wells, liquid_names, volumes):
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 --------------------------------------------------
# ---------------------------------------------------------------

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 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
@@ -176,7 +176,7 @@ class PRCXI9300Handler(LiquidHandlerAbstract):
super().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)
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}")
self.api_client.load_solution(solution_id)
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
def check_channels(cls, use_channels: List[int]) -> List[int]:
@@ -890,6 +894,26 @@ class PRCXI9300Api:
def start(self) -> bool:
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:
payload = json.dumps(
{"ServiceName": service, "MethodName": method, "Paramters": params or []}, separators=(",", ":")

View File

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

View File

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

View File

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

View File

@@ -706,7 +706,20 @@ class HostNode(BaseROS2DeviceNode):
raise ValueError(f"ActionClient {action_id} not found.")
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)
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"] = {}
if not content.get("extra"): # MagicCode
content["extra"] = {}
if "pose" not in content:
content["pose"] = content.pop("position", {})
if "position" in content:
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))
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:
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)