Skip to content

Commit 5b44b6f

Browse files
authored
STAR.minimum_traversal_height (#398)
1 parent b56128a commit 5b44b6f

File tree

2 files changed

+71
-41
lines changed

2 files changed

+71
-41
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
151151
- `spread: Literal["wide", "tight"]` for single-resource multi-channel aspirations/dispenses (https://github.com/PyLabRobot/pylabrobot/pull/378)
152152
- `STAR.request_volume_in_tip` (https://github.com/PyLabRobot/pylabrobot/pull/376)
153153
- `ItemizedResource.{row,column}` (https://github.com/PyLabRobot/pylabrobot/pull/384)
154+
- `STAR.set_minimum_iswap_traversal_height` and `STAR.set_minimum_channel_traversal_height` (https://github.com/PyLabRobot/pylabrobot/pull/398)
154155

155156
### Deprecated
156157

@@ -163,6 +164,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
163164
- 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)
164165
- `Cor_6_wellplate_Fl` (https://github.com/PyLabRobot/pylabrobot/pull/311)
165166
- `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)
167+
- `STAR.set_minimum_traversal_height` (https://github.com/PyLabRobot/pylabrobot/pull/398)
166168

167169
### Fixed
168170

pylabrobot/liquid_handling/backends/hamilton/STAR.py

Lines changed: 69 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
import logging
55
import re
66
from abc import ABCMeta
7-
from contextlib import asynccontextmanager
7+
from contextlib import asynccontextmanager, contextmanager
88
from typing import (
99
Callable,
1010
Dict,
@@ -93,7 +93,7 @@ def need_iswap_parked(method: Callable):
9393
async def wrapper(self: "STAR", *args, **kwargs):
9494
if self.iswap_installed and not self.iswap_parked:
9595
await self.park_iswap(
96-
minimum_traverse_height_at_beginning_of_a_command=int(self._traversal_height * 10)
96+
minimum_traverse_height_at_beginning_of_a_command=int(self._iswap_traversal_height * 10)
9797
)
9898

9999
result = await method(self, *args, **kwargs)
@@ -1164,7 +1164,8 @@ def __init__(
11641164
self._num_channels: Optional[int] = None
11651165
self._core_parked: Optional[bool] = None
11661166
self._extended_conf: Optional[dict] = None
1167-
self._traversal_height: float = 245.0
1167+
self._channel_traversal_height: float = 245.0
1168+
self._iswap_traversal_height: float = 245.0
11681169
self.core_adjustment = Coordinate.zero()
11691170
self._unsafe = UnSafe(self)
11701171

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

11851186
def set_minimum_traversal_height(self, traversal_height: float):
1186-
"""Set the minimum traversal height for the robot.
1187+
raise NotImplementedError(
1188+
"set_minimum_traversal_height is depricated. use set_minimum_channel_traversal_height or "
1189+
"set_minimum_iswap_traversal_height instead."
1190+
)
1191+
1192+
def set_minimum_channel_traversal_height(self, traversal_height: float):
1193+
"""Set the minimum traversal height for the pip channels.
11871194
11881195
This refers to the bottom of the pipetting channel when no tip is present, or the bottom of the
11891196
tip when a tip is present. This value will be used as the default value for the
@@ -1193,7 +1200,24 @@ def set_minimum_traversal_height(self, traversal_height: float):
11931200

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

1196-
self._traversal_height = traversal_height
1203+
self._channel_traversal_height = traversal_height
1204+
1205+
def set_minimum_iswap_traversal_height(self, traversal_height: float):
1206+
"""Set the minimum traversal height for the iswap."""
1207+
1208+
assert 0 < traversal_height < 285, "Traversal height must be between 0 and 285 mm"
1209+
1210+
self._iswap_traversal_height = traversal_height
1211+
1212+
@contextmanager
1213+
def iswap_minimum_traversal_height(self, traversal_height: float):
1214+
orig = self._iswap_traversal_height
1215+
self._iswap_traversal_height = traversal_height
1216+
try:
1217+
yield
1218+
except Exception as e:
1219+
self._iswap_traversal_height = orig
1220+
raise e
11971221

11981222
@property
11991223
def module_id_length(self):
@@ -1367,7 +1391,7 @@ async def setup(
13671391
await self.initialize_pipetting_channels(
13681392
x_positions=[self.extended_conf["xw"]], # Tip eject waste X position.
13691393
y_positions=y_positions,
1370-
begin_of_tip_deposit_process=int(self._traversal_height * 10),
1394+
begin_of_tip_deposit_process=int(self._channel_traversal_height * 10),
13711395
end_of_tip_deposit_process=1220,
13721396
z_position_at_end_of_a_command=3600,
13731397
tip_pattern=[True] * self.num_channels,
@@ -1388,15 +1412,15 @@ async def setup(
13881412
await self.initialize_iswap()
13891413

13901414
await self.park_iswap(
1391-
minimum_traverse_height_at_beginning_of_a_command=int(self._traversal_height * 10)
1415+
minimum_traverse_height_at_beginning_of_a_command=int(self._iswap_traversal_height * 10)
13921416
)
13931417

13941418
if self.core96_head_installed and not skip_core96_head:
13951419
core96_head_initialized = await self.request_core_96_head_initialization_status()
13961420
if not core96_head_initialized:
13971421
await self.initialize_core_96_head(
13981422
trash96=self.deck.get_trash_area96(),
1399-
z_position_at_the_command_end=self._traversal_height,
1423+
z_position_at_the_command_end=self._channel_traversal_height,
14001424
)
14011425

14021426
# After setup, STAR will have thrown out anything mounted on the pipetting channels, including
@@ -1449,7 +1473,7 @@ async def pick_up_tips(
14491473
else round(end_tip_pick_up_process * 10)
14501474
)
14511475
minimum_traverse_height_at_beginning_of_a_command = (
1452-
round(self._traversal_height * 10)
1476+
round(self._channel_traversal_height * 10)
14531477
if minimum_traverse_height_at_beginning_of_a_command is None
14541478
else round(minimum_traverse_height_at_beginning_of_a_command * 10)
14551479
)
@@ -1526,12 +1550,12 @@ async def drop_tips(
15261550
)
15271551

15281552
minimum_traverse_height_at_beginning_of_a_command = (
1529-
round(self._traversal_height * 10)
1553+
round(self._channel_traversal_height * 10)
15301554
if minimum_traverse_height_at_beginning_of_a_command is None
15311555
else round(minimum_traverse_height_at_beginning_of_a_command * 10)
15321556
)
15331557
z_position_at_end_of_a_command = (
1534-
round(self._traversal_height * 10)
1558+
round(self._channel_traversal_height * 10)
15351559
if z_position_at_end_of_a_command is None
15361560
else round(z_position_at_end_of_a_command * 10)
15371561
)
@@ -1869,9 +1893,9 @@ async def aspirate(
18691893
ratio_liquid_rise_to_tip_deep_in=ratio_liquid_rise_to_tip_deep_in,
18701894
immersion_depth_2nd_section=[round(id_ * 10) for id_ in immersion_depth_2nd_section],
18711895
minimum_traverse_height_at_beginning_of_a_command=round(
1872-
(minimum_traverse_height_at_beginning_of_a_command or self._traversal_height) * 10
1896+
(minimum_traverse_height_at_beginning_of_a_command or self._channel_traversal_height) * 10
18731897
),
1874-
min_z_endpos=round((min_z_endpos or self._traversal_height) * 10),
1898+
min_z_endpos=round((min_z_endpos or self._channel_traversal_height) * 10),
18751899
)
18761900
except STARFirmwareError as e:
18771901
if plr_e := convert_star_firmware_error_to_plr_error(e):
@@ -2129,9 +2153,9 @@ async def dispense(
21292153
],
21302154
limit_curve_index=limit_curve_index,
21312155
minimum_traverse_height_at_beginning_of_a_command=round(
2132-
(minimum_traverse_height_at_beginning_of_a_command or self._traversal_height) * 10
2156+
(minimum_traverse_height_at_beginning_of_a_command or self._channel_traversal_height) * 10
21332157
),
2134-
min_z_endpos=round((min_z_endpos or self._traversal_height) * 10),
2158+
min_z_endpos=round((min_z_endpos or self._channel_traversal_height) * 10),
21352159
side_touch_off_distance=side_touch_off_distance,
21362160
)
21372161
except STARFirmwareError as e:
@@ -2167,9 +2191,11 @@ async def pick_up_tips96(
21672191
tip_pickup_method=tip_pickup_method,
21682192
z_deposit_position=round(z_deposit_position * 10),
21692193
minimum_traverse_height_at_beginning_of_a_command=round(
2170-
(minimum_traverse_height_at_beginning_of_a_command or self._traversal_height) * 10
2194+
(minimum_traverse_height_at_beginning_of_a_command or self._channel_traversal_height) * 10
2195+
),
2196+
minimum_height_command_end=round(
2197+
(minimum_height_command_end or self._channel_traversal_height) * 10
21712198
),
2172-
minimum_height_command_end=round((minimum_height_command_end or self._traversal_height) * 10),
21732199
)
21742200

21752201
async def drop_tips96(
@@ -2194,9 +2220,11 @@ async def drop_tips96(
21942220
y_position=round(position.y * 10),
21952221
z_deposit_position=round(z_deposit_position * 10),
21962222
minimum_traverse_height_at_beginning_of_a_command=round(
2197-
(minimum_traverse_height_at_beginning_of_a_command or self._traversal_height) * 10
2223+
(minimum_traverse_height_at_beginning_of_a_command or self._channel_traversal_height) * 10
2224+
),
2225+
minimum_height_command_end=round(
2226+
(minimum_height_command_end or self._channel_traversal_height) * 10
21982227
),
2199-
minimum_height_command_end=round((minimum_height_command_end or self._traversal_height) * 10),
22002228
)
22012229

22022230
async def aspirate96(
@@ -2345,9 +2373,9 @@ async def aspirate96(
23452373
y_positions=round(position.y * 10),
23462374
aspiration_type=aspiration_type,
23472375
minimum_traverse_height_at_beginning_of_a_command=round(
2348-
(minimum_traverse_height_at_beginning_of_a_command or self._traversal_height) * 10
2376+
(minimum_traverse_height_at_beginning_of_a_command or self._channel_traversal_height) * 10
23492377
),
2350-
minimal_end_height=round((minimal_end_height or self._traversal_height) * 10),
2378+
minimal_end_height=round((minimal_end_height or self._channel_traversal_height) * 10),
23512379
lld_search_height=round(lld_search_height * 10),
23522380
liquid_surface_at_function_without_lld=round(liquid_height * 10),
23532381
pull_out_distance_to_take_transport_air_in_function_without_lld=round(
@@ -2509,9 +2537,9 @@ async def dispense96(
25092537
x_direction=0,
25102538
y_position=round(position.y * 10),
25112539
minimum_traverse_height_at_beginning_of_a_command=round(
2512-
(minimum_traverse_height_at_beginning_of_a_command or self._traversal_height) * 10
2540+
(minimum_traverse_height_at_beginning_of_a_command or self._channel_traversal_height) * 10
25132541
),
2514-
minimal_end_height=round((minimal_end_height or self._traversal_height) * 10),
2542+
minimal_end_height=round((minimal_end_height or self._channel_traversal_height) * 10),
25152543
lld_search_height=round(lld_search_height * 10),
25162544
liquid_surface_at_function_without_lld=round(liquid_height * 10),
25172545
pull_out_distance_to_take_transport_air_in_function_without_lld=round(
@@ -2649,10 +2677,10 @@ async def core_pick_up_resource(
26492677
plate_width=round(grip_width * 10) - 30,
26502678
grip_strength=grip_strength,
26512679
minimum_traverse_height_at_beginning_of_a_command=round(
2652-
(minimum_traverse_height_at_beginning_of_a_command or self._traversal_height) * 10
2680+
(minimum_traverse_height_at_beginning_of_a_command or self._channel_traversal_height) * 10
26532681
),
26542682
minimum_z_position_at_the_command_end=round(
2655-
(minimum_z_position_at_the_command_end or self._traversal_height) * 10
2683+
(minimum_z_position_at_the_command_end or self._channel_traversal_height) * 10
26562684
),
26572685
)
26582686

@@ -2689,7 +2717,7 @@ async def core_move_picked_up_resource(
26892717
z_position=round(center.z * 10),
26902718
z_speed=round(z_speed * 10),
26912719
minimum_traverse_height_at_beginning_of_a_command=round(
2692-
(minimum_traverse_height_at_beginning_of_a_command or self._traversal_height) * 10
2720+
(minimum_traverse_height_at_beginning_of_a_command or self._channel_traversal_height) * 10
26932721
),
26942722
)
26952723

@@ -2732,10 +2760,10 @@ async def core_release_picked_up_resource(
27322760
z_speed=500,
27332761
open_gripper_position=round(grip_width * 10) + 30,
27342762
minimum_traverse_height_at_beginning_of_a_command=round(
2735-
(minimum_traverse_height_at_beginning_of_a_command or self._traversal_height) * 10
2763+
(minimum_traverse_height_at_beginning_of_a_command or self._channel_traversal_height) * 10
27362764
),
27372765
z_position_at_the_command_end=round(
2738-
(z_position_at_the_command_end or self._traversal_height) * 10
2766+
(z_position_at_the_command_end or self._channel_traversal_height) * 10
27392767
),
27402768
return_tool=return_tool,
27412769
)
@@ -2786,9 +2814,9 @@ async def pick_up_resource(
27862814
z -= pickup.pickup_distance_from_top
27872815

27882816
traverse_height_at_beginning = (
2789-
minimum_traverse_height_at_beginning_of_a_command or self._traversal_height
2817+
minimum_traverse_height_at_beginning_of_a_command or self._iswap_traversal_height
27902818
)
2791-
z_position_at_the_command_end = z_position_at_the_command_end or self._traversal_height
2819+
z_position_at_the_command_end = z_position_at_the_command_end or self._iswap_traversal_height
27922820

27932821
if open_gripper_position is None:
27942822
if use_unsafe_hotel:
@@ -2851,8 +2879,8 @@ async def pick_up_resource(
28512879
resource=pickup.resource,
28522880
pickup_distance_from_top=pickup.pickup_distance_from_top,
28532881
offset=pickup.offset,
2854-
minimum_traverse_height_at_beginning_of_a_command=self._traversal_height,
2855-
minimum_z_position_at_the_command_end=self._traversal_height,
2882+
minimum_traverse_height_at_beginning_of_a_command=self._channel_traversal_height,
2883+
minimum_z_position_at_the_command_end=self._channel_traversal_height,
28562884
channel_1=channel_1,
28572885
channel_2=channel_2,
28582886
grip_strength=core_grip_strength,
@@ -2868,7 +2896,7 @@ async def move_picked_up_resource(
28682896
location=move.location,
28692897
resource=move.resource,
28702898
grip_direction=move.gripped_direction,
2871-
minimum_traverse_height_at_beginning_of_a_command=self._traversal_height,
2899+
minimum_traverse_height_at_beginning_of_a_command=self._iswap_traversal_height,
28722900
collision_control_level=1,
28732901
acceleration_index_high_acc=4,
28742902
acceleration_index_low_acc=1,
@@ -2877,7 +2905,7 @@ async def move_picked_up_resource(
28772905
await self.core_move_picked_up_resource(
28782906
location=move.location,
28792907
resource=move.resource,
2880-
minimum_traverse_height_at_beginning_of_a_command=self._traversal_height,
2908+
minimum_traverse_height_at_beginning_of_a_command=self._channel_traversal_height,
28812909
acceleration_index=4,
28822910
)
28832911

@@ -2897,9 +2925,9 @@ async def drop_resource(
28972925
):
28982926
if use_arm == "iswap":
28992927
traversal_height_start = (
2900-
minimum_traverse_height_at_beginning_of_a_command or self._traversal_height
2928+
minimum_traverse_height_at_beginning_of_a_command or self._iswap_traversal_height
29012929
)
2902-
z_position_at_the_command_end = z_position_at_the_command_end or self._traversal_height
2930+
z_position_at_the_command_end = z_position_at_the_command_end or self._iswap_traversal_height
29032931
assert (
29042932
drop.resource.get_absolute_rotation().x == 0
29052933
and drop.resource.get_absolute_rotation().y == 0
@@ -2996,8 +3024,8 @@ async def drop_resource(
29963024
resource=drop.resource,
29973025
offset=drop.offset,
29983026
pickup_distance_from_top=drop.pickup_distance_from_top,
2999-
minimum_traverse_height_at_beginning_of_a_command=self._traversal_height,
3000-
z_position_at_the_command_end=self._traversal_height,
3027+
minimum_traverse_height_at_beginning_of_a_command=self._channel_traversal_height,
3028+
z_position_at_the_command_end=self._channel_traversal_height,
30013029
# int(previous_location.z + move.resource.get_size_z() / 2) * 10,
30023030
return_tool=return_core_gripper,
30033031
)
@@ -4665,7 +4693,7 @@ async def get_core(self, p1: int, p2: int):
46654693
pb=f"{p2:02}",
46664694
tp=f"{2350 + self.core_adjustment.z:04}",
46674695
tz=f"{2250 + self.core_adjustment.z:04}",
4668-
th=round(self._traversal_height * 10),
4696+
th=round(self._channel_traversal_height * 10),
46694697
tt="14",
46704698
)
46714699
self._core_parked = False
@@ -4691,8 +4719,8 @@ async def put_core(self):
46914719
yb=f"{1065 + self.core_adjustment.y:04}",
46924720
tp=f"{2150 + self.core_adjustment.z:04}",
46934721
tz=f"{2050 + self.core_adjustment.z:04}",
4694-
th=round(self._traversal_height * 10),
4695-
te=round(self._traversal_height * 10),
4722+
th=round(self._channel_traversal_height * 10),
4723+
te=round(self._channel_traversal_height * 10),
46964724
)
46974725
self._core_parked = True
46984726
return command_output

0 commit comments

Comments
 (0)