Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
- `spread: Literal["wide", "tight"]` for single-resource multi-channel aspirations/dispenses (https://github.com/PyLabRobot/pylabrobot/pull/378)
- `STAR.request_volume_in_tip` (https://github.com/PyLabRobot/pylabrobot/pull/376)
- `ItemizedResource.{row,column}` (https://github.com/PyLabRobot/pylabrobot/pull/384)
- `STAR.set_minimum_iswap_traversal_height` and `STAR.set_minimum_channel_traversal_height` (https://github.com/PyLabRobot/pylabrobot/pull/398)

### Deprecated

Expand All @@ -163,6 +164,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
- Resource definitions with `_L` and `_P`, it is easy enough to use the stem and `.rotated(z=90)` for `_P` (https://github.com/PyLabRobot/pylabrobot/pull/288)
- `Cor_6_wellplate_Fl` (https://github.com/PyLabRobot/pylabrobot/pull/311)
- `AGenBio_1_wellplate_Fl` -> `AGenBio_1_troughplate_190000uL_Fl`, `AGenBio_4_wellplate_Vb` -> `AGenBio_4_troughplate_75000_Vb` (https://github.com/PyLabRobot/pylabrobot/pull/319)
- `STAR.set_minimum_traversal_height` (https://github.com/PyLabRobot/pylabrobot/pull/398)

### Fixed

Expand Down
110 changes: 69 additions & 41 deletions pylabrobot/liquid_handling/backends/hamilton/STAR.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import logging
import re
from abc import ABCMeta
from contextlib import asynccontextmanager
from contextlib import asynccontextmanager, contextmanager
from typing import (
Callable,
Dict,
Expand Down Expand Up @@ -93,7 +93,7 @@ def need_iswap_parked(method: Callable):
async def wrapper(self: "STAR", *args, **kwargs):
if self.iswap_installed and not self.iswap_parked:
await self.park_iswap(
minimum_traverse_height_at_beginning_of_a_command=int(self._traversal_height * 10)
minimum_traverse_height_at_beginning_of_a_command=int(self._iswap_traversal_height * 10)
)

result = await method(self, *args, **kwargs)
Expand Down Expand Up @@ -1164,7 +1164,8 @@ def __init__(
self._num_channels: Optional[int] = None
self._core_parked: Optional[bool] = None
self._extended_conf: Optional[dict] = None
self._traversal_height: float = 245.0
self._channel_traversal_height: float = 245.0
self._iswap_traversal_height: float = 245.0
self.core_adjustment = Coordinate.zero()
self._unsafe = UnSafe(self)

Expand All @@ -1183,7 +1184,13 @@ def num_channels(self) -> int:
return self._num_channels

def set_minimum_traversal_height(self, traversal_height: float):
"""Set the minimum traversal height for the robot.
raise NotImplementedError(
"set_minimum_traversal_height is depricated. use set_minimum_channel_traversal_height or "
"set_minimum_iswap_traversal_height instead."
)

def set_minimum_channel_traversal_height(self, traversal_height: float):
"""Set the minimum traversal height for the pip channels.

This refers to the bottom of the pipetting channel when no tip is present, or the bottom of the
tip when a tip is present. This value will be used as the default value for the
Expand All @@ -1193,7 +1200,24 @@ def set_minimum_traversal_height(self, traversal_height: float):

assert 0 < traversal_height < 285, "Traversal height must be between 0 and 285 mm"

self._traversal_height = traversal_height
self._channel_traversal_height = traversal_height

def set_minimum_iswap_traversal_height(self, traversal_height: float):
"""Set the minimum traversal height for the iswap."""

assert 0 < traversal_height < 285, "Traversal height must be between 0 and 285 mm"

self._iswap_traversal_height = traversal_height

@contextmanager
def iswap_minimum_traversal_height(self, traversal_height: float):
orig = self._iswap_traversal_height
self._iswap_traversal_height = traversal_height
try:
yield
except Exception as e:
self._iswap_traversal_height = orig
raise e

@property
def module_id_length(self):
Expand Down Expand Up @@ -1367,7 +1391,7 @@ async def setup(
await self.initialize_pipetting_channels(
x_positions=[self.extended_conf["xw"]], # Tip eject waste X position.
y_positions=y_positions,
begin_of_tip_deposit_process=int(self._traversal_height * 10),
begin_of_tip_deposit_process=int(self._channel_traversal_height * 10),
end_of_tip_deposit_process=1220,
z_position_at_end_of_a_command=3600,
tip_pattern=[True] * self.num_channels,
Expand All @@ -1388,15 +1412,15 @@ async def setup(
await self.initialize_iswap()

await self.park_iswap(
minimum_traverse_height_at_beginning_of_a_command=int(self._traversal_height * 10)
minimum_traverse_height_at_beginning_of_a_command=int(self._iswap_traversal_height * 10)
)

if self.core96_head_installed and not skip_core96_head:
core96_head_initialized = await self.request_core_96_head_initialization_status()
if not core96_head_initialized:
await self.initialize_core_96_head(
trash96=self.deck.get_trash_area96(),
z_position_at_the_command_end=self._traversal_height,
z_position_at_the_command_end=self._channel_traversal_height,
)

# After setup, STAR will have thrown out anything mounted on the pipetting channels, including
Expand Down Expand Up @@ -1449,7 +1473,7 @@ async def pick_up_tips(
else round(end_tip_pick_up_process * 10)
)
minimum_traverse_height_at_beginning_of_a_command = (
round(self._traversal_height * 10)
round(self._channel_traversal_height * 10)
if minimum_traverse_height_at_beginning_of_a_command is None
else round(minimum_traverse_height_at_beginning_of_a_command * 10)
)
Expand Down Expand Up @@ -1526,12 +1550,12 @@ async def drop_tips(
)

minimum_traverse_height_at_beginning_of_a_command = (
round(self._traversal_height * 10)
round(self._channel_traversal_height * 10)
if minimum_traverse_height_at_beginning_of_a_command is None
else round(minimum_traverse_height_at_beginning_of_a_command * 10)
)
z_position_at_end_of_a_command = (
round(self._traversal_height * 10)
round(self._channel_traversal_height * 10)
if z_position_at_end_of_a_command is None
else round(z_position_at_end_of_a_command * 10)
)
Expand Down Expand Up @@ -1869,9 +1893,9 @@ async def aspirate(
ratio_liquid_rise_to_tip_deep_in=ratio_liquid_rise_to_tip_deep_in,
immersion_depth_2nd_section=[round(id_ * 10) for id_ in immersion_depth_2nd_section],
minimum_traverse_height_at_beginning_of_a_command=round(
(minimum_traverse_height_at_beginning_of_a_command or self._traversal_height) * 10
(minimum_traverse_height_at_beginning_of_a_command or self._channel_traversal_height) * 10
),
min_z_endpos=round((min_z_endpos or self._traversal_height) * 10),
min_z_endpos=round((min_z_endpos or self._channel_traversal_height) * 10),
)
except STARFirmwareError as e:
if plr_e := convert_star_firmware_error_to_plr_error(e):
Expand Down Expand Up @@ -2129,9 +2153,9 @@ async def dispense(
],
limit_curve_index=limit_curve_index,
minimum_traverse_height_at_beginning_of_a_command=round(
(minimum_traverse_height_at_beginning_of_a_command or self._traversal_height) * 10
(minimum_traverse_height_at_beginning_of_a_command or self._channel_traversal_height) * 10
),
min_z_endpos=round((min_z_endpos or self._traversal_height) * 10),
min_z_endpos=round((min_z_endpos or self._channel_traversal_height) * 10),
side_touch_off_distance=side_touch_off_distance,
)
except STARFirmwareError as e:
Expand Down Expand Up @@ -2167,9 +2191,11 @@ async def pick_up_tips96(
tip_pickup_method=tip_pickup_method,
z_deposit_position=round(z_deposit_position * 10),
minimum_traverse_height_at_beginning_of_a_command=round(
(minimum_traverse_height_at_beginning_of_a_command or self._traversal_height) * 10
(minimum_traverse_height_at_beginning_of_a_command or self._channel_traversal_height) * 10
),
minimum_height_command_end=round(
(minimum_height_command_end or self._channel_traversal_height) * 10
),
minimum_height_command_end=round((minimum_height_command_end or self._traversal_height) * 10),
)

async def drop_tips96(
Expand All @@ -2194,9 +2220,11 @@ async def drop_tips96(
y_position=round(position.y * 10),
z_deposit_position=round(z_deposit_position * 10),
minimum_traverse_height_at_beginning_of_a_command=round(
(minimum_traverse_height_at_beginning_of_a_command or self._traversal_height) * 10
(minimum_traverse_height_at_beginning_of_a_command or self._channel_traversal_height) * 10
),
minimum_height_command_end=round(
(minimum_height_command_end or self._channel_traversal_height) * 10
),
minimum_height_command_end=round((minimum_height_command_end or self._traversal_height) * 10),
)

async def aspirate96(
Expand Down Expand Up @@ -2345,9 +2373,9 @@ async def aspirate96(
y_positions=round(position.y * 10),
aspiration_type=aspiration_type,
minimum_traverse_height_at_beginning_of_a_command=round(
(minimum_traverse_height_at_beginning_of_a_command or self._traversal_height) * 10
(minimum_traverse_height_at_beginning_of_a_command or self._channel_traversal_height) * 10
),
minimal_end_height=round((minimal_end_height or self._traversal_height) * 10),
minimal_end_height=round((minimal_end_height or self._channel_traversal_height) * 10),
lld_search_height=round(lld_search_height * 10),
liquid_surface_at_function_without_lld=round(liquid_height * 10),
pull_out_distance_to_take_transport_air_in_function_without_lld=round(
Expand Down Expand Up @@ -2509,9 +2537,9 @@ async def dispense96(
x_direction=0,
y_position=round(position.y * 10),
minimum_traverse_height_at_beginning_of_a_command=round(
(minimum_traverse_height_at_beginning_of_a_command or self._traversal_height) * 10
(minimum_traverse_height_at_beginning_of_a_command or self._channel_traversal_height) * 10
),
minimal_end_height=round((minimal_end_height or self._traversal_height) * 10),
minimal_end_height=round((minimal_end_height or self._channel_traversal_height) * 10),
lld_search_height=round(lld_search_height * 10),
liquid_surface_at_function_without_lld=round(liquid_height * 10),
pull_out_distance_to_take_transport_air_in_function_without_lld=round(
Expand Down Expand Up @@ -2649,10 +2677,10 @@ async def core_pick_up_resource(
plate_width=round(grip_width * 10) - 30,
grip_strength=grip_strength,
minimum_traverse_height_at_beginning_of_a_command=round(
(minimum_traverse_height_at_beginning_of_a_command or self._traversal_height) * 10
(minimum_traverse_height_at_beginning_of_a_command or self._channel_traversal_height) * 10
),
minimum_z_position_at_the_command_end=round(
(minimum_z_position_at_the_command_end or self._traversal_height) * 10
(minimum_z_position_at_the_command_end or self._channel_traversal_height) * 10
),
)

Expand Down Expand Up @@ -2689,7 +2717,7 @@ async def core_move_picked_up_resource(
z_position=round(center.z * 10),
z_speed=round(z_speed * 10),
minimum_traverse_height_at_beginning_of_a_command=round(
(minimum_traverse_height_at_beginning_of_a_command or self._traversal_height) * 10
(minimum_traverse_height_at_beginning_of_a_command or self._channel_traversal_height) * 10
),
)

Expand Down Expand Up @@ -2732,10 +2760,10 @@ async def core_release_picked_up_resource(
z_speed=500,
open_gripper_position=round(grip_width * 10) + 30,
minimum_traverse_height_at_beginning_of_a_command=round(
(minimum_traverse_height_at_beginning_of_a_command or self._traversal_height) * 10
(minimum_traverse_height_at_beginning_of_a_command or self._channel_traversal_height) * 10
),
z_position_at_the_command_end=round(
(z_position_at_the_command_end or self._traversal_height) * 10
(z_position_at_the_command_end or self._channel_traversal_height) * 10
),
return_tool=return_tool,
)
Expand Down Expand Up @@ -2786,9 +2814,9 @@ async def pick_up_resource(
z -= pickup.pickup_distance_from_top

traverse_height_at_beginning = (
minimum_traverse_height_at_beginning_of_a_command or self._traversal_height
minimum_traverse_height_at_beginning_of_a_command or self._iswap_traversal_height
)
z_position_at_the_command_end = z_position_at_the_command_end or self._traversal_height
z_position_at_the_command_end = z_position_at_the_command_end or self._iswap_traversal_height

if open_gripper_position is None:
if use_unsafe_hotel:
Expand Down Expand Up @@ -2851,8 +2879,8 @@ async def pick_up_resource(
resource=pickup.resource,
pickup_distance_from_top=pickup.pickup_distance_from_top,
offset=pickup.offset,
minimum_traverse_height_at_beginning_of_a_command=self._traversal_height,
minimum_z_position_at_the_command_end=self._traversal_height,
minimum_traverse_height_at_beginning_of_a_command=self._channel_traversal_height,
minimum_z_position_at_the_command_end=self._channel_traversal_height,
channel_1=channel_1,
channel_2=channel_2,
grip_strength=core_grip_strength,
Expand All @@ -2868,7 +2896,7 @@ async def move_picked_up_resource(
location=move.location,
resource=move.resource,
grip_direction=move.gripped_direction,
minimum_traverse_height_at_beginning_of_a_command=self._traversal_height,
minimum_traverse_height_at_beginning_of_a_command=self._iswap_traversal_height,
collision_control_level=1,
acceleration_index_high_acc=4,
acceleration_index_low_acc=1,
Expand All @@ -2877,7 +2905,7 @@ async def move_picked_up_resource(
await self.core_move_picked_up_resource(
location=move.location,
resource=move.resource,
minimum_traverse_height_at_beginning_of_a_command=self._traversal_height,
minimum_traverse_height_at_beginning_of_a_command=self._channel_traversal_height,
acceleration_index=4,
)

Expand All @@ -2897,9 +2925,9 @@ async def drop_resource(
):
if use_arm == "iswap":
traversal_height_start = (
minimum_traverse_height_at_beginning_of_a_command or self._traversal_height
minimum_traverse_height_at_beginning_of_a_command or self._iswap_traversal_height
)
z_position_at_the_command_end = z_position_at_the_command_end or self._traversal_height
z_position_at_the_command_end = z_position_at_the_command_end or self._iswap_traversal_height
assert (
drop.resource.get_absolute_rotation().x == 0
and drop.resource.get_absolute_rotation().y == 0
Expand Down Expand Up @@ -2996,8 +3024,8 @@ async def drop_resource(
resource=drop.resource,
offset=drop.offset,
pickup_distance_from_top=drop.pickup_distance_from_top,
minimum_traverse_height_at_beginning_of_a_command=self._traversal_height,
z_position_at_the_command_end=self._traversal_height,
minimum_traverse_height_at_beginning_of_a_command=self._channel_traversal_height,
z_position_at_the_command_end=self._channel_traversal_height,
# int(previous_location.z + move.resource.get_size_z() / 2) * 10,
return_tool=return_core_gripper,
)
Expand Down Expand Up @@ -4665,7 +4693,7 @@ async def get_core(self, p1: int, p2: int):
pb=f"{p2:02}",
tp=f"{2350 + self.core_adjustment.z:04}",
tz=f"{2250 + self.core_adjustment.z:04}",
th=round(self._traversal_height * 10),
th=round(self._channel_traversal_height * 10),
tt="14",
)
self._core_parked = False
Expand All @@ -4691,8 +4719,8 @@ async def put_core(self):
yb=f"{1065 + self.core_adjustment.y:04}",
tp=f"{2150 + self.core_adjustment.z:04}",
tz=f"{2050 + self.core_adjustment.z:04}",
th=round(self._traversal_height * 10),
te=round(self._traversal_height * 10),
th=round(self._channel_traversal_height * 10),
te=round(self._channel_traversal_height * 10),
)
self._core_parked = True
return command_output
Expand Down