opentrons 8.2.0__py2.py3-none-any.whl → 8.2.0a0__py2.py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of opentrons might be problematic. Click here for more details.

Files changed (62) hide show
  1. opentrons/drivers/absorbance_reader/async_byonoy.py +5 -6
  2. opentrons/hardware_control/backends/ot3utils.py +0 -1
  3. opentrons/hardware_control/modules/absorbance_reader.py +0 -2
  4. opentrons/hardware_control/ot3api.py +5 -5
  5. opentrons/hardware_control/protocols/position_estimator.py +1 -3
  6. opentrons/hardware_control/types.py +0 -2
  7. opentrons/legacy_commands/helpers.py +2 -8
  8. opentrons/protocol_api/core/engine/labware.py +2 -10
  9. opentrons/protocol_api/core/engine/module_core.py +1 -38
  10. opentrons/protocol_api/core/engine/pipette_movement_conflict.py +5 -12
  11. opentrons/protocol_api/core/engine/protocol.py +30 -5
  12. opentrons/protocol_api/core/labware.py +0 -4
  13. opentrons/protocol_api/core/legacy/legacy_labware_core.py +0 -5
  14. opentrons/protocol_api/core/legacy/legacy_protocol_core.py +0 -1
  15. opentrons/protocol_api/core/protocol.py +0 -1
  16. opentrons/protocol_api/module_contexts.py +26 -69
  17. opentrons/protocol_api/protocol_context.py +2 -12
  18. opentrons/protocol_engine/actions/__init__.py +2 -0
  19. opentrons/protocol_engine/actions/actions.py +12 -0
  20. opentrons/protocol_engine/clients/sync_client.py +6 -0
  21. opentrons/protocol_engine/commands/absorbance_reader/close_lid.py +31 -18
  22. opentrons/protocol_engine/commands/absorbance_reader/initialize.py +7 -19
  23. opentrons/protocol_engine/commands/absorbance_reader/open_lid.py +29 -17
  24. opentrons/protocol_engine/commands/absorbance_reader/read.py +0 -4
  25. opentrons/protocol_engine/commands/aspirate_in_place.py +3 -3
  26. opentrons/protocol_engine/commands/command.py +1 -3
  27. opentrons/protocol_engine/commands/dispense_in_place.py +1 -1
  28. opentrons/protocol_engine/commands/drop_tip.py +1 -2
  29. opentrons/protocol_engine/commands/drop_tip_in_place.py +2 -7
  30. opentrons/protocol_engine/commands/load_labware.py +0 -9
  31. opentrons/protocol_engine/commands/load_module.py +39 -0
  32. opentrons/protocol_engine/commands/move_labware.py +4 -49
  33. opentrons/protocol_engine/commands/pick_up_tip.py +1 -1
  34. opentrons/protocol_engine/commands/pipetting_common.py +1 -8
  35. opentrons/protocol_engine/commands/unsafe/unsafe_place_labware.py +35 -49
  36. opentrons/protocol_engine/commands/unsafe/unsafe_ungrip_labware.py +1 -3
  37. opentrons/protocol_engine/commands/unsafe/update_position_estimators.py +1 -5
  38. opentrons/protocol_engine/create_protocol_engine.py +1 -18
  39. opentrons/protocol_engine/errors/__init__.py +0 -2
  40. opentrons/protocol_engine/errors/error_occurrence.py +3 -8
  41. opentrons/protocol_engine/errors/exceptions.py +0 -13
  42. opentrons/protocol_engine/execution/labware_movement.py +21 -69
  43. opentrons/protocol_engine/execution/movement.py +4 -9
  44. opentrons/protocol_engine/protocol_engine.py +7 -0
  45. opentrons/protocol_engine/resources/deck_data_provider.py +39 -0
  46. opentrons/protocol_engine/resources/file_provider.py +7 -11
  47. opentrons/protocol_engine/resources/fixture_validation.py +1 -6
  48. opentrons/protocol_engine/state/geometry.py +49 -91
  49. opentrons/protocol_engine/state/labware.py +25 -102
  50. opentrons/protocol_engine/state/module_substates/absorbance_reader_substate.py +1 -3
  51. opentrons/protocol_engine/state/modules.py +80 -53
  52. opentrons/protocol_engine/state/motion.py +5 -17
  53. opentrons/protocol_engine/state/update_types.py +0 -16
  54. opentrons/protocol_runner/run_orchestrator.py +0 -15
  55. opentrons/protocols/parameters/csv_parameter_interface.py +1 -3
  56. opentrons/util/logging_config.py +1 -1
  57. {opentrons-8.2.0.dist-info → opentrons-8.2.0a0.dist-info}/METADATA +4 -4
  58. {opentrons-8.2.0.dist-info → opentrons-8.2.0a0.dist-info}/RECORD +62 -62
  59. {opentrons-8.2.0.dist-info → opentrons-8.2.0a0.dist-info}/LICENSE +0 -0
  60. {opentrons-8.2.0.dist-info → opentrons-8.2.0a0.dist-info}/WHEEL +0 -0
  61. {opentrons-8.2.0.dist-info → opentrons-8.2.0a0.dist-info}/entry_points.txt +0 -0
  62. {opentrons-8.2.0.dist-info → opentrons-8.2.0a0.dist-info}/top_level.txt +0 -0
@@ -262,33 +262,32 @@ class GeometryView:
262
262
  return min_travel_z
263
263
 
264
264
  def get_labware_parent_nominal_position(self, labware_id: str) -> Point:
265
- """Get the position of the labware's uncalibrated parent (deck slot, module, or another labware)."""
265
+ """Get the position of the labware's uncalibrated parent slot (deck, module, or another labware)."""
266
266
  try:
267
267
  addressable_area_name = self.get_ancestor_slot_name(labware_id).id
268
268
  except errors.LocationIsStagingSlotError:
269
269
  addressable_area_name = self._get_staging_slot_name(labware_id)
270
270
  except errors.LocationIsLidDockSlotError:
271
271
  addressable_area_name = self._get_lid_dock_slot_name(labware_id)
272
- parent_pos = self._addressable_areas.get_addressable_area_position(
272
+ slot_pos = self._addressable_areas.get_addressable_area_position(
273
273
  addressable_area_name
274
274
  )
275
+ labware_data = self._labware.get(labware_id)
275
276
 
276
- offset_from_parent = self._get_offset_from_parent(
277
- child_definition=self._labware.get_definition(labware_id),
278
- parent=self._labware.get(labware_id).location,
279
- )
277
+ offset = self._get_labware_position_offset(labware_id, labware_data.location)
280
278
 
281
279
  return Point(
282
- parent_pos.x + offset_from_parent.x,
283
- parent_pos.y + offset_from_parent.y,
284
- parent_pos.z + offset_from_parent.z,
280
+ slot_pos.x + offset.x,
281
+ slot_pos.y + offset.y,
282
+ slot_pos.z + offset.z,
285
283
  )
286
284
 
287
- def _get_offset_from_parent(
288
- self, child_definition: LabwareDefinition, parent: LabwareLocation
285
+ def _get_labware_position_offset(
286
+ self, labware_id: str, labware_location: LabwareLocation
289
287
  ) -> LabwareOffsetVector:
290
- """Gets the offset vector of a labware placed on the given location.
288
+ """Gets the offset vector of a labware on the given location.
291
289
 
290
+ NOTE: Not to be confused with LPC offset.
292
291
  - For labware on Deck Slot: returns an offset of (0, 0, 0)
293
292
  - For labware on a Module: returns the nominal offset for the labware's position
294
293
  when placed on the specified module (using slot-transformed labwareOffset
@@ -299,42 +298,40 @@ class GeometryView:
299
298
  on modules as well as stacking overlaps.
300
299
  Does not include module calibration offset or LPC offset.
301
300
  """
302
- if isinstance(parent, (AddressableAreaLocation, DeckSlotLocation)):
301
+ if isinstance(labware_location, (AddressableAreaLocation, DeckSlotLocation)):
303
302
  return LabwareOffsetVector(x=0, y=0, z=0)
304
- elif isinstance(parent, ModuleLocation):
305
- module_id = parent.moduleId
306
- module_to_child = self._modules.get_nominal_offset_to_child(
303
+ elif isinstance(labware_location, ModuleLocation):
304
+ module_id = labware_location.moduleId
305
+ module_offset = self._modules.get_nominal_module_offset(
307
306
  module_id=module_id, addressable_areas=self._addressable_areas
308
307
  )
309
308
  module_model = self._modules.get_connected_model(module_id)
310
309
  stacking_overlap = self._labware.get_module_overlap_offsets(
311
- child_definition, module_model
310
+ labware_id, module_model
312
311
  )
313
312
  return LabwareOffsetVector(
314
- x=module_to_child.x - stacking_overlap.x,
315
- y=module_to_child.y - stacking_overlap.y,
316
- z=module_to_child.z - stacking_overlap.z,
317
- )
318
- elif isinstance(parent, OnLabwareLocation):
319
- on_labware = self._labware.get(parent.labwareId)
320
- on_labware_dimensions = self._labware.get_dimensions(
321
- labware_id=on_labware.id
313
+ x=module_offset.x - stacking_overlap.x,
314
+ y=module_offset.y - stacking_overlap.y,
315
+ z=module_offset.z - stacking_overlap.z,
322
316
  )
317
+ elif isinstance(labware_location, OnLabwareLocation):
318
+ on_labware = self._labware.get(labware_location.labwareId)
319
+ on_labware_dimensions = self._labware.get_dimensions(on_labware.id)
323
320
  stacking_overlap = self._labware.get_labware_overlap_offsets(
324
- definition=child_definition, below_labware_name=on_labware.loadName
321
+ labware_id=labware_id, below_labware_name=on_labware.loadName
325
322
  )
326
323
  labware_offset = LabwareOffsetVector(
327
324
  x=stacking_overlap.x,
328
325
  y=stacking_overlap.y,
329
326
  z=on_labware_dimensions.z - stacking_overlap.z,
330
327
  )
331
- return labware_offset + self._get_offset_from_parent(
332
- self._labware.get_definition(on_labware.id), on_labware.location
328
+ return labware_offset + self._get_labware_position_offset(
329
+ on_labware.id, on_labware.location
333
330
  )
334
331
  else:
335
332
  raise errors.LabwareNotOnDeckError(
336
- "Cannot access labware since it is not on the deck. "
337
- "Either it has been loaded off-deck or its been moved off-deck."
333
+ f"Cannot access labware {labware_id} since it is not on the deck. "
334
+ f"Either it has been loaded off-deck or its been moved off-deck."
338
335
  )
339
336
 
340
337
  def _normalize_module_calibration_offset(
@@ -712,12 +709,10 @@ class GeometryView:
712
709
  assert isinstance(labware_location, AddressableAreaLocation)
713
710
  return labware_location.addressableAreaName
714
711
 
715
- def get_ancestor_slot_name(
716
- self, labware_id: str
717
- ) -> Union[DeckSlotName, StagingSlotName]:
712
+ def get_ancestor_slot_name(self, labware_id: str) -> DeckSlotName:
718
713
  """Get the slot name of the labware or the module that the labware is on."""
719
714
  labware = self._labware.get(labware_id)
720
- slot_name: Union[DeckSlotName, StagingSlotName]
715
+ slot_name: DeckSlotName
721
716
 
722
717
  if isinstance(labware.location, DeckSlotLocation):
723
718
  slot_name = labware.location.slotName
@@ -729,14 +724,18 @@ class GeometryView:
729
724
  slot_name = self.get_ancestor_slot_name(below_labware_id)
730
725
  elif isinstance(labware.location, AddressableAreaLocation):
731
726
  area_name = labware.location.addressableAreaName
727
+ # TODO we might want to eventually return some sort of staging slot name when we're ready to work through
728
+ # the linting nightmare it will create
732
729
  if self._labware.is_absorbance_reader_lid(labware_id):
733
730
  raise errors.LocationIsLidDockSlotError(
734
731
  "Cannot get ancestor slot name for labware on lid dock slot."
735
732
  )
736
- elif fixture_validation.is_staging_slot(area_name):
737
- slot_name = StagingSlotName.from_primitive(area_name)
738
- else:
739
- slot_name = DeckSlotName.from_primitive(area_name)
733
+ if fixture_validation.is_staging_slot(area_name):
734
+ raise errors.LocationIsStagingSlotError(
735
+ "Cannot get ancestor slot name for labware on staging slot."
736
+ )
737
+ raise errors.LocationIs
738
+ slot_name = DeckSlotName.from_primitive(area_name)
740
739
  elif labware.location == OFF_DECK_LOCATION:
741
740
  raise errors.LabwareNotOnDeckError(
742
741
  f"Labware {labware_id} does not have a slot associated with it"
@@ -769,7 +768,7 @@ class GeometryView:
769
768
 
770
769
  def get_labware_grip_point(
771
770
  self,
772
- labware_definition: LabwareDefinition,
771
+ labware_id: str,
773
772
  location: Union[
774
773
  DeckSlotLocation, ModuleLocation, OnLabwareLocation, AddressableAreaLocation
775
774
  ],
@@ -785,7 +784,7 @@ class GeometryView:
785
784
  z-position of labware bottom + grip height from labware bottom.
786
785
  """
787
786
  grip_height_from_labware_bottom = (
788
- self._labware.get_grip_height_from_labware_bottom(labware_definition)
787
+ self._labware.get_grip_height_from_labware_bottom(labware_id)
789
788
  )
790
789
  location_name: str
791
790
 
@@ -811,9 +810,7 @@ class GeometryView:
811
810
  ).slotName.id
812
811
  else: # OnLabwareLocation
813
812
  location_name = self.get_ancestor_slot_name(location.labwareId).id
814
- labware_offset = self._get_offset_from_parent(
815
- child_definition=labware_definition, parent=location
816
- )
813
+ labware_offset = self._get_labware_position_offset(labware_id, location)
817
814
  # Get the calibrated offset if the on labware location is on top of a module, otherwise return empty one
818
815
  cal_offset = self._get_calibrated_module_offset(location)
819
816
  offset = LabwareOffsetVector(
@@ -832,9 +829,7 @@ class GeometryView:
832
829
  )
833
830
 
834
831
  def get_extra_waypoints(
835
- self,
836
- location: Optional[CurrentPipetteLocation],
837
- to_slot: Union[DeckSlotName, StagingSlotName],
832
+ self, location: Optional[CurrentPipetteLocation], to_slot: DeckSlotName
838
833
  ) -> List[Tuple[float, float]]:
839
834
  """Get extra waypoints for movement if thermocycler needs to be dodged."""
840
835
  if location is not None:
@@ -893,10 +888,8 @@ class GeometryView:
893
888
  return maybe_labware or maybe_module or maybe_fixture or None
894
889
 
895
890
  @staticmethod
896
- def get_slot_column(slot_name: Union[DeckSlotName, StagingSlotName]) -> int:
891
+ def get_slot_column(slot_name: DeckSlotName) -> int:
897
892
  """Get the column number for the specified slot."""
898
- if isinstance(slot_name, StagingSlotName):
899
- return 4
900
893
  row_col_name = slot_name.to_ot3_equivalent()
901
894
  slot_name_match = WELL_NAME_PATTERN.match(row_col_name.value)
902
895
  assert (
@@ -1177,13 +1170,7 @@ class GeometryView:
1177
1170
  )
1178
1171
 
1179
1172
  assert isinstance(
1180
- ancestor,
1181
- (
1182
- DeckSlotLocation,
1183
- ModuleLocation,
1184
- OnLabwareLocation,
1185
- AddressableAreaLocation,
1186
- ),
1173
+ ancestor, (DeckSlotLocation, ModuleLocation, OnLabwareLocation)
1187
1174
  ), "No gripper offsets for off-deck labware"
1188
1175
  return (
1189
1176
  direct_parent_offset.pickUpOffset
@@ -1208,7 +1195,6 @@ class GeometryView:
1208
1195
  extra_offset = LabwareOffsetVector(x=0, y=0, z=0)
1209
1196
  if (
1210
1197
  isinstance(ancestor, ModuleLocation)
1211
- # todo(mm, 2024-11-06): Do not access private module state; only use public ModuleView methods.
1212
1198
  and self._modules._state.requested_model_by_id[ancestor.moduleId]
1213
1199
  == ModuleModel.THERMOCYCLER_MODULE_V2
1214
1200
  and labware_validation.validate_definition_is_lid(current_labware)
@@ -1231,13 +1217,7 @@ class GeometryView:
1231
1217
  )
1232
1218
 
1233
1219
  assert isinstance(
1234
- ancestor,
1235
- (
1236
- DeckSlotLocation,
1237
- ModuleLocation,
1238
- OnLabwareLocation,
1239
- AddressableAreaLocation,
1240
- ),
1220
+ ancestor, (DeckSlotLocation, ModuleLocation, OnLabwareLocation)
1241
1221
  ), "No gripper offsets for off-deck labware"
1242
1222
  return (
1243
1223
  direct_parent_offset.dropOffset
@@ -1247,23 +1227,6 @@ class GeometryView:
1247
1227
  + extra_offset
1248
1228
  )
1249
1229
 
1250
- # todo(mm, 2024-11-05): This may be incorrect because it does not take the following
1251
- # offsets into account, which *are* taken into account for the actual gripper movement:
1252
- #
1253
- # * The pickup offset in the definition of the parent of the gripped labware.
1254
- # * The "additional offset" or "user offset", e.g. the `pickUpOffset` and `dropOffset`
1255
- # params in the `moveLabware` command.
1256
- #
1257
- # And this *does* take these extra offsets into account:
1258
- #
1259
- # * The labware's Labware Position Check offset
1260
- #
1261
- # For robustness, we should combine this with `get_gripper_labware_movement_waypoints()`.
1262
- #
1263
- # We should also be more explicit about which offsets act to move the gripper paddles
1264
- # relative to the gripped labware, and which offsets act to change how the gripped
1265
- # labware sits atop its parent. Those have different effects on how far the gripped
1266
- # labware juts beyond the paddles while it's in transit.
1267
1230
  def check_gripper_labware_tip_collision(
1268
1231
  self,
1269
1232
  gripper_homed_position_z: float,
@@ -1271,22 +1234,18 @@ class GeometryView:
1271
1234
  current_location: OnDeckLabwareLocation,
1272
1235
  ) -> None:
1273
1236
  """Check for potential collision of tips against labware to be lifted."""
1274
- labware_definition = self._labware.get_definition(labware_id)
1237
+ # TODO(cb, 2024-01-22): Remove the 1 and 8 channel special case once we are doing X axis validation
1275
1238
  pipettes = self._pipettes.get_all()
1276
1239
  for pipette in pipettes:
1277
- # TODO(cb, 2024-01-22): Remove the 1 and 8 channel special case once we are doing X axis validation
1278
1240
  if self._pipettes.get_channels(pipette.id) in [1, 8]:
1279
1241
  return
1280
1242
 
1281
1243
  tip = self._pipettes.get_attached_tip(pipette.id)
1282
1244
  if tip:
1283
- # NOTE: This call to get_labware_highest_z() uses the labware's LPC offset,
1284
- # which is an inconsistency between this and the actual gripper movement.
1285
- # See the todo comment above this function.
1286
1245
  labware_top_z_when_gripped = gripper_homed_position_z + (
1287
1246
  self.get_labware_highest_z(labware_id=labware_id)
1288
1247
  - self.get_labware_grip_point(
1289
- labware_definition=labware_definition, location=current_location
1248
+ labware_id=labware_id, location=current_location
1290
1249
  ).z
1291
1250
  )
1292
1251
  # TODO(cb, 2024-01-18): Utilizing the nozzle map and labware X coordinates verify if collisions will occur on the X axis (analysis will use hard coded data to measure from the gripper critical point to the pipette mount)
@@ -1294,7 +1253,7 @@ class GeometryView:
1294
1253
  _PIPETTE_HOMED_POSITION_Z - tip.length
1295
1254
  ) < labware_top_z_when_gripped:
1296
1255
  raise LabwareMovementNotAllowedError(
1297
- f"Cannot move labware '{labware_definition.parameters.loadName}' when {int(tip.volume)} µL tips are attached."
1256
+ f"Cannot move labware '{self._labware.get(labware_id).loadName}' when {int(tip.volume)} µL tips are attached."
1298
1257
  )
1299
1258
  return
1300
1259
 
@@ -1334,7 +1293,6 @@ class GeometryView:
1334
1293
  DeckSlotLocation,
1335
1294
  ModuleLocation,
1336
1295
  AddressableAreaLocation,
1337
- OnLabwareLocation,
1338
1296
  ),
1339
1297
  ), "No gripper offsets for off-deck labware"
1340
1298
 
@@ -1348,11 +1306,11 @@ class GeometryView:
1348
1306
  module_loc = self._modules.get_location(parent_location.moduleId)
1349
1307
  slot_name = module_loc.slotName
1350
1308
 
1351
- slot_based_offset = self._labware.get_child_gripper_offsets(
1309
+ slot_based_offset = self._labware.get_labware_gripper_offsets(
1352
1310
  labware_id=labware_id, slot_name=slot_name.to_ot3_equivalent()
1353
1311
  )
1354
1312
 
1355
- return slot_based_offset or self._labware.get_child_gripper_offsets(
1313
+ return slot_based_offset or self._labware.get_labware_gripper_offsets(
1356
1314
  labware_id=labware_id, slot_name=None
1357
1315
  )
1358
1316
 
@@ -13,7 +13,6 @@ from typing import (
13
13
  NamedTuple,
14
14
  cast,
15
15
  Union,
16
- overload,
17
16
  )
18
17
 
19
18
  from opentrons.protocol_engine.state import update_types
@@ -82,10 +81,6 @@ _RIGHT_SIDE_SLOTS = {
82
81
  }
83
82
 
84
83
 
85
- # The max height of the labware that can fit in a plate reader
86
- _PLATE_READER_MAX_LABWARE_Z_MM = 16
87
-
88
-
89
84
  class LabwareLoadParams(NamedTuple):
90
85
  """Parameters required to load a labware in Protocol Engine."""
91
86
 
@@ -232,11 +227,10 @@ class LabwareStore(HasState[LabwareState], HandlesActions):
232
227
  if labware_location_update.new_location:
233
228
  new_location = labware_location_update.new_location
234
229
 
235
- if isinstance(new_location, AddressableAreaLocation) and (
236
- fixture_validation.is_gripper_waste_chute(
237
- new_location.addressableAreaName
238
- )
239
- or fixture_validation.is_trash(new_location.addressableAreaName)
230
+ if isinstance(
231
+ new_location, AddressableAreaLocation
232
+ ) and fixture_validation.is_gripper_waste_chute(
233
+ new_location.addressableAreaName
240
234
  ):
241
235
  # If a labware has been moved into a waste chute it's been chuted away and is now technically off deck
242
236
  new_location = OFF_DECK_LOCATION
@@ -631,26 +625,10 @@ class LabwareView(HasState[LabwareState]):
631
625
  definition = self.get_definition(labware_id)
632
626
  return definition.parameters.loadName
633
627
 
634
- @overload
635
- def get_dimensions(self, *, labware_definition: LabwareDefinition) -> Dimensions:
636
- pass
637
-
638
- @overload
639
- def get_dimensions(self, *, labware_id: str) -> Dimensions:
640
- pass
641
-
642
- def get_dimensions(
643
- self,
644
- *,
645
- labware_definition: LabwareDefinition | None = None,
646
- labware_id: str | None = None,
647
- ) -> Dimensions:
628
+ def get_dimensions(self, labware_id: str) -> Dimensions:
648
629
  """Get the labware's dimensions."""
649
- if labware_definition is None:
650
- assert labware_id is not None # From our @overloads.
651
- labware_definition = self.get_definition(labware_id)
652
-
653
- dims = labware_definition.dimensions
630
+ definition = self.get_definition(labware_id)
631
+ dims = definition.dimensions
654
632
 
655
633
  return Dimensions(
656
634
  x=dims.xDimension,
@@ -659,9 +637,10 @@ class LabwareView(HasState[LabwareState]):
659
637
  )
660
638
 
661
639
  def get_labware_overlap_offsets(
662
- self, definition: LabwareDefinition, below_labware_name: str
640
+ self, labware_id: str, below_labware_name: str
663
641
  ) -> OverlapOffset:
664
642
  """Get the labware's overlap with requested labware's load name."""
643
+ definition = self.get_definition(labware_id)
665
644
  if below_labware_name in definition.stackingOffsetWithLabware.keys():
666
645
  stacking_overlap = definition.stackingOffsetWithLabware.get(
667
646
  below_labware_name, OverlapOffset(x=0, y=0, z=0)
@@ -675,9 +654,10 @@ class LabwareView(HasState[LabwareState]):
675
654
  )
676
655
 
677
656
  def get_module_overlap_offsets(
678
- self, definition: LabwareDefinition, module_model: ModuleModel
657
+ self, labware_id: str, module_model: ModuleModel
679
658
  ) -> OverlapOffset:
680
659
  """Get the labware's overlap with requested module model."""
660
+ definition = self.get_definition(labware_id)
681
661
  stacking_overlap = definition.stackingOffsetWithModule.get(
682
662
  str(module_model.value)
683
663
  )
@@ -837,24 +817,6 @@ class LabwareView(HasState[LabwareState]):
837
817
  f"Labware {labware.loadName} is already present at {location}."
838
818
  )
839
819
 
840
- def raise_if_labware_incompatible_with_plate_reader(
841
- self,
842
- labware_definition: LabwareDefinition,
843
- ) -> None:
844
- """Raise an error if the labware is not compatible with the plate reader."""
845
- load_name = labware_definition.parameters.loadName
846
- number_of_wells = len(labware_definition.wells)
847
- if number_of_wells != 96:
848
- raise errors.LabwareMovementNotAllowedError(
849
- f"Cannot move '{load_name}' into plate reader because the"
850
- f" labware contains {number_of_wells} wells where 96 wells is expected."
851
- )
852
- elif labware_definition.dimensions.zDimension > _PLATE_READER_MAX_LABWARE_Z_MM:
853
- raise errors.LabwareMovementNotAllowedError(
854
- f"Cannot move '{load_name}' into plate reader because the"
855
- f" maximum allowed labware height is {_PLATE_READER_MAX_LABWARE_Z_MM}mm."
856
- )
857
-
858
820
  def raise_if_labware_cannot_be_stacked( # noqa: C901
859
821
  self, top_labware_definition: LabwareDefinition, bottom_labware_id: str
860
822
  ) -> None:
@@ -938,60 +900,22 @@ class LabwareView(HasState[LabwareState]):
938
900
  else None
939
901
  )
940
902
 
941
- def get_absorbance_reader_lid_definition(self) -> LabwareDefinition:
942
- """Return the special labware definition for the plate reader lid.
943
-
944
- See todo comments in `create_protocol_engine().
945
- """
946
- # NOTE: This needs to stay in sync with create_protocol_engine().
947
- return self._state.definitions_by_uri[
948
- "opentrons/opentrons_flex_lid_absorbance_plate_reader_module/1"
949
- ]
950
-
951
- @overload
952
- def get_child_gripper_offsets(
903
+ def get_labware_gripper_offsets(
953
904
  self,
954
- *,
955
- labware_definition: LabwareDefinition,
956
- slot_name: Optional[DeckSlotName],
957
- ) -> Optional[LabwareMovementOffsetData]:
958
- pass
959
-
960
- @overload
961
- def get_child_gripper_offsets(
962
- self, *, labware_id: str, slot_name: Optional[DeckSlotName]
963
- ) -> Optional[LabwareMovementOffsetData]:
964
- pass
965
-
966
- def get_child_gripper_offsets(
967
- self,
968
- *,
969
- labware_definition: Optional[LabwareDefinition] = None,
970
- labware_id: Optional[str] = None,
905
+ labware_id: str,
971
906
  slot_name: Optional[DeckSlotName],
972
907
  ) -> Optional[LabwareMovementOffsetData]:
973
- """Get the grip offsets that a labware says should be applied to children stacked atop it.
974
-
975
- Params:
976
- labware_id: The ID of a parent labware (atop which another labware, the child, will be stacked).
977
- slot_name: The ancestor slot that the parent labware is ultimately loaded into,
978
- perhaps after going through a module in the middle.
908
+ """Get the labware's gripper offsets of the specified type.
979
909
 
980
910
  Returns:
981
- If `slot_name` is provided, returns the gripper offsets that the parent labware definition
911
+ If `slot_name` is provided, returns the gripper offsets that the labware definition
982
912
  specifies just for that slot, or `None` if the labware definition doesn't have an
983
913
  exact match.
984
914
 
985
- If `slot_name` is `None`, returns the gripper offsets that the parent labware
915
+ If `slot_name` is `None`, returns the gripper offsets that the labware
986
916
  definition designates as "default," or `None` if it doesn't designate any as such.
987
917
  """
988
- if labware_id is not None:
989
- labware_definition = self.get_definition(labware_id)
990
- else:
991
- # Should be ensured by our @overloads.
992
- assert labware_definition is not None
993
-
994
- parsed_offsets = labware_definition.gripperOffsets
918
+ parsed_offsets = self.get_definition(labware_id).gripperOffsets
995
919
  offset_key = slot_name.id if slot_name else "default"
996
920
 
997
921
  if parsed_offsets is None or offset_key not in parsed_offsets:
@@ -1006,22 +930,20 @@ class LabwareView(HasState[LabwareState]):
1006
930
  ),
1007
931
  )
1008
932
 
1009
- def get_grip_force(self, labware_definition: LabwareDefinition) -> float:
933
+ def get_grip_force(self, labware_id: str) -> float:
1010
934
  """Get the recommended grip force for gripping labware using gripper."""
1011
- recommended_force = labware_definition.gripForce
935
+ recommended_force = self.get_definition(labware_id).gripForce
1012
936
  return (
1013
937
  recommended_force if recommended_force is not None else LABWARE_GRIP_FORCE
1014
938
  )
1015
939
 
1016
- def get_grip_height_from_labware_bottom(
1017
- self, labware_definition: LabwareDefinition
1018
- ) -> float:
940
+ def get_grip_height_from_labware_bottom(self, labware_id: str) -> float:
1019
941
  """Get the recommended grip height from labware bottom, if present."""
1020
- recommended_height = labware_definition.gripHeightFromLabwareBottom
942
+ recommended_height = self.get_definition(labware_id).gripHeightFromLabwareBottom
1021
943
  return (
1022
944
  recommended_height
1023
945
  if recommended_height is not None
1024
- else self.get_dimensions(labware_definition=labware_definition).z / 2
946
+ else self.get_dimensions(labware_id).z / 2
1025
947
  )
1026
948
 
1027
949
  @staticmethod
@@ -1064,7 +986,7 @@ class LabwareView(HasState[LabwareState]):
1064
986
  def _max_z_of_well(well_defn: WellDefinition) -> float:
1065
987
  return well_defn.z + well_defn.depth
1066
988
 
1067
- def get_well_bbox(self, labware_definition: LabwareDefinition) -> Dimensions:
989
+ def get_well_bbox(self, labware_id: str) -> Dimensions:
1068
990
  """Get the bounding box implied by the wells.
1069
991
 
1070
992
  The bounding box of the labware that is implied by the wells is that required
@@ -1075,13 +997,14 @@ class LabwareView(HasState[LabwareState]):
1075
997
  This is used for the specific purpose of finding the reasonable uncertainty bounds of
1076
998
  where and how a gripper will interact with a labware.
1077
999
  """
1000
+ defn = self.get_definition(labware_id)
1078
1001
  max_x: Optional[float] = None
1079
1002
  min_x: Optional[float] = None
1080
1003
  max_y: Optional[float] = None
1081
1004
  min_y: Optional[float] = None
1082
1005
  max_z: Optional[float] = None
1083
1006
 
1084
- for well in labware_definition.wells.values():
1007
+ for well in defn.wells.values():
1085
1008
  well_max_x = self._max_x_of_well(well)
1086
1009
  well_min_x = self._min_x_of_well(well)
1087
1010
  well_max_y = self._max_y_of_well(well)
@@ -9,9 +9,6 @@ AbsorbanceReaderLidId = NewType("AbsorbanceReaderLidId", str)
9
9
  AbsorbanceReaderMeasureMode = NewType("AbsorbanceReaderMeasureMode", str)
10
10
 
11
11
 
12
- # todo(mm, 2024-11-08): frozen=True is getting pretty painful because ModuleStore has
13
- # no type-safe way to modify just a single attribute. Consider unfreezing this
14
- # (taking care to ensure that consumers of ModuleView still only get a read-only view).
15
12
  @dataclass(frozen=True)
16
13
  class AbsorbanceReaderSubState:
17
14
  """Absorbance-Plate-Reader-specific state."""
@@ -24,6 +21,7 @@ class AbsorbanceReaderSubState:
24
21
  configured_wavelengths: Optional[List[int]]
25
22
  measure_mode: Optional[AbsorbanceReaderMeasureMode]
26
23
  reference_wavelength: Optional[int]
24
+ lid_id: Optional[str]
27
25
 
28
26
  def raise_if_lid_status_not_expected(self, lid_on_expected: bool) -> None:
29
27
  """Raise if the lid status is not correct."""