Skip to content
53 changes: 39 additions & 14 deletions pylabrobot/liquid_handling/backends/hamilton/STAR_backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -1193,6 +1193,7 @@ def __init__(

self._iswap_parked: Optional[bool] = None
self._num_channels: Optional[int] = None
self._channel_minimum_y_spacing: float = 9.0
self._core_parked: Optional[bool] = None
self._extended_conf: Optional[dict] = None
self._channel_traversal_height: float = 245.0
Expand Down Expand Up @@ -1470,6 +1471,9 @@ async def setup(
async def set_up_pip():
if (not initialized or any(tip_presences)) and not skip_pip:
await self.initialize_pip()
self._channel_minimum_y_spacing = (
9.0 # TODO: identify from machine directly to override default
)

async def set_up_autoload():
if self.autoload_installed and not skip_autoload:
Expand Down Expand Up @@ -4172,8 +4176,13 @@ async def core_check_resource_exists_at_location_center(

center = location + resource.centers()[0] + offset
y_width_to_gripper_bump = resource.get_absolute_size_y() - gripper_y_margin * 2
assert 9 <= y_width_to_gripper_bump <= round(resource.get_absolute_size_y()), (
f"width between channels must be between 9 and {resource.get_absolute_size_y()} mm"
assert (
self._channel_minimum_y_spacing
<= y_width_to_gripper_bump
<= round(resource.get_absolute_size_y())
), (
f"width between channels must be between {self._channel_minimum_y_spacing} and "
f"{resource.get_absolute_size_y()} mm"
" (i.e. the minimal distance between channels and the max y size of the resource"
)

Expand Down Expand Up @@ -9968,8 +9977,12 @@ async def clld_probe_y_position_using_channel(
channel_idx_plus_one_y_pos = 6
# Insight: STAR machines appear to lose connection to a channel below y-position=6 mm

max_safe_upper_y_pos = channel_idx_minus_one_y_pos - 9
max_safe_lower_y_pos = channel_idx_plus_one_y_pos + 9 if channel_idx_plus_one_y_pos != 0 else 6
max_safe_upper_y_pos = channel_idx_minus_one_y_pos - self._channel_minimum_y_spacing
max_safe_lower_y_pos = (
channel_idx_plus_one_y_pos + self._channel_minimum_y_spacing
if channel_idx_plus_one_y_pos != 0
else 6
)

# Enable safe start and end positions
if start_pos_search:
Expand Down Expand Up @@ -10050,7 +10063,9 @@ async def clld_probe_y_position_using_channel(
else: # next channel
adjacent_y_pos = await self.request_y_pos_channel_n(channel_idx + 1)

max_safe_y_mov_dist_post_detection = detected_material_y_pos - adjacent_y_pos - 9.0
max_safe_y_mov_dist_post_detection = (
detected_material_y_pos - adjacent_y_pos - self._channel_minimum_y_spacing
)
move_target = detected_material_y_pos - min(
post_detection_dist, max_safe_y_mov_dist_post_detection
)
Expand All @@ -10061,7 +10076,9 @@ async def clld_probe_y_position_using_channel(
else: # previous channel
adjacent_y_pos = await self.request_y_pos_channel_n(channel_idx - 1)

max_safe_y_mov_dist_post_detection = adjacent_y_pos - detected_material_y_pos - 9.0
max_safe_y_mov_dist_post_detection = (
adjacent_y_pos - detected_material_y_pos - self._channel_minimum_y_spacing
)
move_target = detected_material_y_pos + min(
post_detection_dist, max_safe_y_mov_dist_post_detection
)
Expand Down Expand Up @@ -10946,7 +10963,7 @@ async def get_channels_y_positions(self) -> Dict[int, float]:
elif 5.8 <= y_positions[-1] < 6:
y_positions[-1] = 6.0

min_diff = 9.0
min_diff = self._channel_minimum_y_spacing
for i in range(len(y_positions) - 2, -1, -1):
if y_positions[i] - y_positions[i + 1] < min_diff:
y_positions[i] = y_positions[i + 1] + min_diff
Expand Down Expand Up @@ -10985,17 +11002,25 @@ async def position_channels_in_y_direction(self, ys: Dict[int, float], make_spac
use_channels = list(ys.keys())
back_channel = min(use_channels)
for channel_idx in range(back_channel, 0, -1):
if (channel_locations[channel_idx - 1] - channel_locations[channel_idx]) < 9:
channel_locations[channel_idx - 1] = channel_locations[channel_idx] + 9
if (
channel_locations[channel_idx - 1] - channel_locations[channel_idx]
) < self._channel_minimum_y_spacing:
channel_locations[channel_idx - 1] = (
channel_locations[channel_idx] + self._channel_minimum_y_spacing
)

# Similarly for the channels to the front of `front_channel`, make sure they are all
# spaced >=9mm apart. This time, we iterate from back (closest to `front_channel`)
# to the front (lh.backend.num_channels - 1), and put each channel >=9mm before the
# one behind it.
# spaced >= channel_minimum_y_spacing (usually 9mm) apart. This time, we iterate from
# back (closest to `front_channel`) to the front (lh.backend.num_channels - 1), and
# put each channel >= channel_minimum_y_spacing before the one behind it.
front_channel = max(use_channels)
for channel_idx in range(front_channel, self.num_channels - 1):
if (channel_locations[channel_idx] - channel_locations[channel_idx + 1]) < 9:
channel_locations[channel_idx + 1] = channel_locations[channel_idx] - 9
if (
channel_locations[channel_idx] - channel_locations[channel_idx + 1]
) < self._channel_minimum_y_spacing:
channel_locations[channel_idx + 1] = (
channel_locations[channel_idx] - self._channel_minimum_y_spacing
)

# Quick checks before movement.
if channel_locations[0] > 650:
Expand Down