From 8307b3493af63e84a353bf4f7676b2087102fb11 Mon Sep 17 00:00:00 2001 From: Eric <113262615+ericguan04@users.noreply.github.com> Date: Tue, 3 Jun 2025 16:13:09 -0400 Subject: [PATCH 1/5] fix: resource stack handling for Lid in LiquidHandler and PlateHolder. Specifically, Lid will now be assigned to parent Plate if top item of ResourceStack is Plate. Change to carrier.py ensures that ResourceStack is inferred correctly (up to two levels of parent above, since Lid may be child of plate, or directly a child of ResourceStack). Future stability improvements: Geometric Plate comptability check before assigining as child. If the plate will not slot along skirting, it should not register as a child. --- .vscode/settings.json | 3 ++- pylabrobot/liquid_handling/liquid_handler.py | 23 ++++++++++++++------ pylabrobot/resources/carrier.py | 12 +++++++++- 3 files changed, 29 insertions(+), 9 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 714c508189..df11ff2a8d 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -45,5 +45,6 @@ "editor.wordWrap": "bounded", "editor.wordWrapColumn": 100 }, - "mypy.runUsingActiveInterpreter": true + "mypy.runUsingActiveInterpreter": true, + "ros.distro": "humble" } diff --git a/pylabrobot/liquid_handling/liquid_handler.py b/pylabrobot/liquid_handling/liquid_handler.py index 0224803212..80fd9abb17 100644 --- a/pylabrobot/liquid_handling/liquid_handler.py +++ b/pylabrobot/liquid_handling/liquid_handler.py @@ -1813,7 +1813,7 @@ async def drop_resource( ) # get the location of the destination - if isinstance(destination, ResourceStack): + if isinstance(destination, ResourceStack) and not isinstance(resource, Lid): assert ( destination.direction == "z" ), "Only ResourceStacks with direction 'z' are currently supported" @@ -1835,13 +1835,22 @@ async def drop_resource( resource.rotated(z=resource_rotation_wrt_destination_wrt_local) ).rotated(destination.get_absolute_rotation()) to_location = destination.get_absolute_location() + adjusted_plate_anchor - elif isinstance(destination, Plate) and isinstance(resource, Lid): + elif isinstance(destination, (Plate, ResourceStack)) and isinstance(resource, Lid): lid = resource - plate_location = destination.get_absolute_location() - child_wrt_parent = destination.get_lid_location( - lid.rotated(z=resource_rotation_wrt_destination_wrt_local) - ).rotated(destination.get_absolute_rotation()) - to_location = plate_location + child_wrt_parent + if isinstance (destination, ResourceStack): + if destination.direction != "z": + raise ValueError("Only ResourceStacks with direction 'z' are currently supported") + top_item = destination.get_top_item() + if isinstance(top_item, Plate): + destination = top_item + else: + to_location = destination.get_absolute_location(z="top") + if isinstance(destination, Plate): + plate_location = destination.get_absolute_location() + child_wrt_parent = destination.get_lid_location( + lid.rotated(z=resource_rotation_wrt_destination_wrt_local) + ).rotated(destination.get_absolute_rotation()) + to_location = plate_location + child_wrt_parent else: to_location = destination.get_absolute_location() diff --git a/pylabrobot/resources/carrier.py b/pylabrobot/resources/carrier.py index 6eabb1de3c..edf7055100 100644 --- a/pylabrobot/resources/carrier.py +++ b/pylabrobot/resources/carrier.py @@ -234,7 +234,17 @@ def _update_resource_stack_location(self, resource: Resource): Args: resource: The Resource on the ResourceStack tht was assigned. """ - resource_stack = resource.parent + + if isinstance(resource, Lid): + lid_parent = resource.parent + if isinstance(lid_parent, ResourceStack): + resource_stack = lid_parent + elif isinstance(lid_parent.parent, ResourceStack): + resource_stack = lid_parent.parent + else: + raise ValueError("ResourceStack not found for Lid") + else: + resource_stack = resource.parent assert isinstance(resource_stack, ResourceStack) if resource_stack.children[0] == resource: resource_stack.location = self.get_default_child_location(resource) From 5acdc82ae5fb6ebae395c624bd5a4c9926b614cd Mon Sep 17 00:00:00 2001 From: Eric <113262615+ericguan04@users.noreply.github.com> Date: Tue, 3 Jun 2025 16:43:56 -0400 Subject: [PATCH 2/5] fix: improve formatting in LiquidHandler class for better readability with ruff --- pylabrobot/liquid_handling/liquid_handler.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pylabrobot/liquid_handling/liquid_handler.py b/pylabrobot/liquid_handling/liquid_handler.py index 80fd9abb17..ababe48504 100644 --- a/pylabrobot/liquid_handling/liquid_handler.py +++ b/pylabrobot/liquid_handling/liquid_handler.py @@ -1837,7 +1837,7 @@ async def drop_resource( to_location = destination.get_absolute_location() + adjusted_plate_anchor elif isinstance(destination, (Plate, ResourceStack)) and isinstance(resource, Lid): lid = resource - if isinstance (destination, ResourceStack): + if isinstance(destination, ResourceStack): if destination.direction != "z": raise ValueError("Only ResourceStacks with direction 'z' are currently supported") top_item = destination.get_top_item() From 0a4eeef269a61e5631791666763a497e60bcba73 Mon Sep 17 00:00:00 2001 From: Eric <113262615+ericguan04@users.noreply.github.com> Date: Tue, 3 Jun 2025 17:21:19 -0400 Subject: [PATCH 3/5] Fixed linting, formatting, and type inspection issues --- pylabrobot/liquid_handling/liquid_handler.py | 11 +++++++---- pylabrobot/resources/carrier.py | 9 ++++++++- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/pylabrobot/liquid_handling/liquid_handler.py b/pylabrobot/liquid_handling/liquid_handler.py index ababe48504..a133cc6838 100644 --- a/pylabrobot/liquid_handling/liquid_handler.py +++ b/pylabrobot/liquid_handling/liquid_handler.py @@ -1840,11 +1840,14 @@ async def drop_resource( if isinstance(destination, ResourceStack): if destination.direction != "z": raise ValueError("Only ResourceStacks with direction 'z' are currently supported") - top_item = destination.get_top_item() - if isinstance(top_item, Plate): - destination = top_item - else: + if len(destination.children) == 0: to_location = destination.get_absolute_location(z="top") + else: + top_item = destination.get_top_item() + if isinstance(top_item, Plate): + destination = top_item + else: + to_location = destination.get_absolute_location(z="top") if isinstance(destination, Plate): plate_location = destination.get_absolute_location() child_wrt_parent = destination.get_lid_location( diff --git a/pylabrobot/resources/carrier.py b/pylabrobot/resources/carrier.py index edf7055100..122c5ed218 100644 --- a/pylabrobot/resources/carrier.py +++ b/pylabrobot/resources/carrier.py @@ -237,6 +237,8 @@ def _update_resource_stack_location(self, resource: Resource): if isinstance(resource, Lid): lid_parent = resource.parent + if lid_parent is None: + raise ValueError("Lid has no parent. ResourceStack not found for Lid") if isinstance(lid_parent, ResourceStack): resource_stack = lid_parent elif isinstance(lid_parent.parent, ResourceStack): @@ -244,8 +246,13 @@ def _update_resource_stack_location(self, resource: Resource): else: raise ValueError("ResourceStack not found for Lid") else: + if resource.parent is None: + raise ValueError("ResourceStack not found for resource") + if not isinstance(resource.parent, ResourceStack): + raise TypeError( + f"Resource {resource} is not a child of a ResourceStack, but of {type(resource.parent)}" + ) resource_stack = resource.parent - assert isinstance(resource_stack, ResourceStack) if resource_stack.children[0] == resource: resource_stack.location = self.get_default_child_location(resource) From 8bb93da3b98468f489708de3e426d50826cf774c Mon Sep 17 00:00:00 2001 From: Eric <113262615+ericguan04@users.noreply.github.com> Date: Thu, 5 Jun 2025 12:31:26 -0400 Subject: [PATCH 4/5] Remove ROS line in .vscode/settings.json --- .vscode/settings.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index df11ff2a8d..714c508189 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -45,6 +45,5 @@ "editor.wordWrap": "bounded", "editor.wordWrapColumn": 100 }, - "mypy.runUsingActiveInterpreter": true, - "ros.distro": "humble" + "mypy.runUsingActiveInterpreter": true } From 3ead5b141ba6c4ace2372e24ccea6a4f2c7f63b6 Mon Sep 17 00:00:00 2001 From: Eric <113262615+ericguan04@users.noreply.github.com> Date: Sat, 7 Jun 2025 23:33:00 -0400 Subject: [PATCH 5/5] Add comment --- pylabrobot/liquid_handling/liquid_handler.py | 1 + 1 file changed, 1 insertion(+) diff --git a/pylabrobot/liquid_handling/liquid_handler.py b/pylabrobot/liquid_handling/liquid_handler.py index a133cc6838..29971517a2 100644 --- a/pylabrobot/liquid_handling/liquid_handler.py +++ b/pylabrobot/liquid_handling/liquid_handler.py @@ -1835,6 +1835,7 @@ async def drop_resource( resource.rotated(z=resource_rotation_wrt_destination_wrt_local) ).rotated(destination.get_absolute_rotation()) to_location = destination.get_absolute_location() + adjusted_plate_anchor + # Automatically assign lid to plate as child when dropping a lid to a plate (on or off resource stack) elif isinstance(destination, (Plate, ResourceStack)) and isinstance(resource, Lid): lid = resource if isinstance(destination, ResourceStack):