diff --git a/unilabos/devices/workstation/bioyond_studio/reaction_station.py b/unilabos/devices/workstation/bioyond_studio/reaction_station.py index d732fafb..3e9a6a6e 100644 --- a/unilabos/devices/workstation/bioyond_studio/reaction_station.py +++ b/unilabos/devices/workstation/bioyond_studio/reaction_station.py @@ -605,7 +605,8 @@ class BioyondReactionStation(BioyondWorkstation): total_params += 1 step_parameters[step_id][action_name].append({ "Key": param_key, - "DisplayValue": param_value + "DisplayValue": param_value, + "Value": param_value }) successful_params += 1 # print(f" ✓ {param_key} = {param_value}") diff --git a/unilabos/devices/workstation/workstation_http_service.py b/unilabos/devices/workstation/workstation_http_service.py index 4565edea..27f869c0 100644 --- a/unilabos/devices/workstation/workstation_http_service.py +++ b/unilabos/devices/workstation/workstation_http_service.py @@ -668,7 +668,7 @@ __all__ = [ if __name__ == "__main__": # 简单测试HTTP服务 - class DummyWorkstation: + class BioyondWorkstation: device_id = "WS-001" def process_step_finish_report(self, report_request): diff --git a/unilabos/registry/registry.py b/unilabos/registry/registry.py index caeafa5c..ecc9f820 100644 --- a/unilabos/registry/registry.py +++ b/unilabos/registry/registry.py @@ -708,6 +708,8 @@ class Registry: for status_name, status_type in device_config["class"]["status_types"].items(): device_config["class"]["status_types"][status_name] = status_str_type_mapping[status_type] for action_name, action_config in device_config["class"]["action_value_mappings"].items(): + if action_config["type"] not in action_str_type_mapping: + continue action_config["type"] = action_str_type_mapping[action_config["type"]] # 添加内置的驱动命令动作 self._add_builtin_actions(device_config, device_id) diff --git a/unilabos/resources/bioyond/warehouses.py b/unilabos/resources/bioyond/warehouses.py index 507b1f2f..266a3666 100644 --- a/unilabos/resources/bioyond/warehouses.py +++ b/unilabos/resources/bioyond/warehouses.py @@ -5,15 +5,15 @@ def bioyond_warehouse_1x4x4(name: str) -> WareHouse: """创建BioYond 4x1x4仓库""" return warehouse_factory( name=name, - num_items_x=1, + num_items_x=4, num_items_y=4, - num_items_z=4, + num_items_z=1, dx=10.0, dy=10.0, dz=10.0, - item_dx=137.0, - item_dy=96.0, - item_dz=120.0, + item_dx=147.0, + item_dy=106.0, + item_dz=130.0, category="warehouse", ) diff --git a/unilabos/resources/graphio.py b/unilabos/resources/graphio.py index 98f6c74e..ada7e8d2 100644 --- a/unilabos/resources/graphio.py +++ b/unilabos/resources/graphio.py @@ -639,25 +639,24 @@ def resource_bioyond_to_plr(bioyond_materials: list[dict], type_mapping: Dict[st # 处理子物料(detail) if material.get("detail") and len(material["detail"]) > 0: + for bottle in reversed(plr_material.children): + plr_material.unassign_child_resource(bottle) child_ids = [] for detail in material["detail"]: number = ( (detail.get("z", 0) - 1) * plr_material.num_items_x * plr_material.num_items_y - + (detail.get("x", 0) - 1) * plr_material.num_items_x - + (detail.get("y", 0) - 1) + + (detail.get("y", 0) - 1) * plr_material.num_items_y + + (detail.get("x", 0) - 1) ) - bottle = plr_material[number] - if detail["name"] in type_mapping: - # plr_material.unassign_child_resource(bottle) - plr_material.sites[number] = None - plr_material[number] = initialize_resource( - {"name": f'{detail["name"]}_{number}', "class": type_mapping[detail["name"]][0]}, resource_type=ResourcePLR + typeName = detail.get("typeName", detail.get("name", "")) + if typeName in type_mapping: + bottle = plr_material[number] = initialize_resource( + {"name": f'{detail["name"]}_{number}', "class": type_mapping[typeName][0]}, resource_type=ResourcePLR ) - else: bottle.tracker.liquids = [ (detail["name"], float(detail.get("quantity", 0)) if detail.get("quantity") else 0) ] - bottle.code = detail.get("code", "") + bottle.code = detail.get("code", "") else: bottle = plr_material[0] if plr_material.capacity > 0 else plr_material bottle.tracker.liquids = [ diff --git a/unilabos/resources/itemized_carrier.py b/unilabos/resources/itemized_carrier.py index 44074b53..7607cf4d 100644 --- a/unilabos/resources/itemized_carrier.py +++ b/unilabos/resources/itemized_carrier.py @@ -74,6 +74,7 @@ class ItemizedCarrier(ResourcePLR): num_items_x: int = 0, num_items_y: int = 0, num_items_z: int = 0, + layout: str = "x-y", sites: Optional[Dict[Union[int, str], Optional[ResourcePLR]]] = None, category: Optional[str] = "carrier", model: Optional[str] = None, @@ -88,6 +89,8 @@ class ItemizedCarrier(ResourcePLR): ) self.num_items = len(sites) self.num_items_x, self.num_items_y, self.num_items_z = num_items_x, num_items_y, num_items_z + self.layout = "z-y" if self.num_items_z > 1 and self.num_items_x == 1 else "x-z" if self.num_items_z > 1 and self.num_items_y == 1 else "x-y" + if isinstance(sites, dict): sites = sites or {} self.sites: List[Optional[ResourcePLR]] = list(sites.values()) @@ -150,7 +153,7 @@ class ItemizedCarrier(ResourcePLR): def assign_resource_to_site(self, resource: ResourcePLR, spot: int): if self.sites[spot] is not None and not isinstance(self.sites[spot], ResourceHolder): raise ValueError(f"spot {spot} already has a resource, {resource}") - self.assign_child_resource(resource, location=self.child_locations.get(str(spot)), spot=spot) + self.assign_child_resource(resource, location=self.child_locations.get(list(self._ordering.keys())[spot]), spot=spot) def unassign_child_resource(self, resource: ResourcePLR): found = False @@ -161,8 +164,9 @@ class ItemizedCarrier(ResourcePLR): break if not found: raise ValueError(f"Resource {resource} is not assigned to this carrier") - if hasattr(resource, "unassign"): - resource.unassign() + super().unassign_child_resource(resource) + # if hasattr(resource, "unassign"): + # resource.unassign() def get_child_identifier(self, child: ResourcePLR): """Get the identifier information for a given child resource. @@ -403,6 +407,7 @@ class ItemizedCarrier(ResourcePLR): "num_items_x": self.num_items_x, "num_items_y": self.num_items_y, "num_items_z": self.num_items_z, + "layout": self.layout, "sites": [{ "label": str(identifier), "visible": True if self[identifier] is not None else False, diff --git a/unilabos/resources/warehouse.py b/unilabos/resources/warehouse.py index 775c55aa..c665b7fa 100644 --- a/unilabos/resources/warehouse.py +++ b/unilabos/resources/warehouse.py @@ -5,10 +5,13 @@ from pylabrobot.resources.carrier import ResourceHolder, create_homogeneous_reso from unilabos.resources.itemized_carrier import ItemizedCarrier, ResourcePLR +LETTERS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + + def warehouse_factory( name: str, - num_items_x: int = 4, - num_items_y: int = 1, + num_items_x: int = 1, + num_items_y: int = 4, num_items_z: int = 4, dx: float = 137.0, dy: float = 96.0, @@ -33,13 +36,16 @@ def warehouse_factory( locations.append(Coordinate(x, y, z)) if removed_positions: locations = [loc for i, loc in enumerate(locations) if i not in removed_positions] - sites = create_homogeneous_resources( + _sites = create_homogeneous_resources( klass=ResourceHolder, locations=locations, resource_size_x=127.0, resource_size_y=86.0, name_prefix=name, ) + len_x, len_y = (num_items_x, num_items_y) if num_items_z == 1 else (num_items_y, num_items_z) if num_items_x == 1 else (num_items_x, num_items_z) + keys = [f"{LETTERS[j]}{i + 1}" for i in range(len_x) for j in range(len_y)] + sites = {i: site for i, site in zip(keys, _sites.values())} return WareHouse( name=name, @@ -68,6 +74,7 @@ class WareHouse(ItemizedCarrier): num_items_x: int, num_items_y: int, num_items_z: int, + layout: str = "x-y", sites: Optional[Dict[Union[int, str], Optional[ResourcePLR]]] = None, category: str = "warehouse", model: Optional[str] = None, @@ -83,6 +90,7 @@ class WareHouse(ItemizedCarrier): num_items_x=num_items_x, num_items_y=num_items_y, num_items_z=num_items_z, + layout=layout, sites=sites, category=category, model=model, diff --git a/unilabos/ros/nodes/resource_tracker.py b/unilabos/ros/nodes/resource_tracker.py index 35d4529a..cd533aab 100644 --- a/unilabos/ros/nodes/resource_tracker.py +++ b/unilabos/ros/nodes/resource_tracker.py @@ -342,6 +342,7 @@ class ResourceTreeSet(object): pos = { "size": {"width": d["size_x"], "height": d["size_y"], "depth": d["size_z"]}, "scale": {"x": 1.0, "y": 1.0, "z": 1.0}, + "layout": d.get("layout", "x-y"), "position": raw_pos, "position3d": raw_pos, "rotation": d["rotation"],