diff --git a/pylabrobot/liquid_handling/backends/hamilton/STAR_backend.py b/pylabrobot/liquid_handling/backends/hamilton/STAR_backend.py index 4fe23b32afa..42a63aa6cb6 100644 --- a/pylabrobot/liquid_handling/backends/hamilton/STAR_backend.py +++ b/pylabrobot/liquid_handling/backends/hamilton/STAR_backend.py @@ -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 @@ -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: @@ -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" ) @@ -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: @@ -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 ) @@ -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 ) @@ -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 @@ -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: