opentrons 8.7.0a6__py3-none-any.whl → 8.7.0a7__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 (144) hide show
  1. opentrons/_version.py +2 -2
  2. opentrons/drivers/asyncio/communication/serial_connection.py +129 -52
  3. opentrons/drivers/heater_shaker/abstract.py +5 -0
  4. opentrons/drivers/heater_shaker/driver.py +10 -0
  5. opentrons/drivers/heater_shaker/simulator.py +4 -0
  6. opentrons/drivers/thermocycler/abstract.py +6 -0
  7. opentrons/drivers/thermocycler/driver.py +61 -10
  8. opentrons/drivers/thermocycler/simulator.py +6 -0
  9. opentrons/hardware_control/api.py +24 -5
  10. opentrons/hardware_control/backends/controller.py +8 -2
  11. opentrons/hardware_control/backends/ot3controller.py +3 -0
  12. opentrons/hardware_control/backends/ot3simulator.py +2 -1
  13. opentrons/hardware_control/backends/simulator.py +2 -1
  14. opentrons/hardware_control/backends/subsystem_manager.py +5 -2
  15. opentrons/hardware_control/emulation/abstract_emulator.py +6 -4
  16. opentrons/hardware_control/emulation/connection_handler.py +8 -5
  17. opentrons/hardware_control/emulation/heater_shaker.py +12 -3
  18. opentrons/hardware_control/emulation/settings.py +1 -1
  19. opentrons/hardware_control/emulation/thermocycler.py +67 -15
  20. opentrons/hardware_control/module_control.py +82 -8
  21. opentrons/hardware_control/modules/__init__.py +3 -0
  22. opentrons/hardware_control/modules/absorbance_reader.py +11 -4
  23. opentrons/hardware_control/modules/flex_stacker.py +38 -9
  24. opentrons/hardware_control/modules/heater_shaker.py +42 -5
  25. opentrons/hardware_control/modules/magdeck.py +8 -4
  26. opentrons/hardware_control/modules/mod_abc.py +13 -5
  27. opentrons/hardware_control/modules/tempdeck.py +25 -5
  28. opentrons/hardware_control/modules/thermocycler.py +68 -11
  29. opentrons/hardware_control/modules/types.py +20 -1
  30. opentrons/hardware_control/modules/utils.py +11 -4
  31. opentrons/hardware_control/nozzle_manager.py +3 -0
  32. opentrons/hardware_control/ot3api.py +26 -5
  33. opentrons/hardware_control/poller.py +22 -8
  34. opentrons/hardware_control/scripts/update_module_fw.py +5 -0
  35. opentrons/hardware_control/types.py +31 -2
  36. opentrons/legacy_commands/module_commands.py +23 -0
  37. opentrons/legacy_commands/protocol_commands.py +20 -0
  38. opentrons/legacy_commands/types.py +80 -0
  39. opentrons/motion_planning/deck_conflict.py +17 -12
  40. opentrons/motion_planning/waypoints.py +15 -29
  41. opentrons/protocol_api/__init__.py +5 -1
  42. opentrons/protocol_api/_types.py +6 -1
  43. opentrons/protocol_api/core/common.py +3 -1
  44. opentrons/protocol_api/core/engine/_default_labware_versions.py +32 -11
  45. opentrons/protocol_api/core/engine/labware.py +8 -1
  46. opentrons/protocol_api/core/engine/module_core.py +75 -8
  47. opentrons/protocol_api/core/engine/protocol.py +18 -1
  48. opentrons/protocol_api/core/engine/tasks.py +48 -0
  49. opentrons/protocol_api/core/engine/well.py +8 -0
  50. opentrons/protocol_api/core/legacy/legacy_module_core.py +24 -4
  51. opentrons/protocol_api/core/legacy/legacy_protocol_core.py +11 -1
  52. opentrons/protocol_api/core/legacy/legacy_well_core.py +4 -0
  53. opentrons/protocol_api/core/legacy/tasks.py +19 -0
  54. opentrons/protocol_api/core/legacy_simulator/legacy_protocol_core.py +14 -2
  55. opentrons/protocol_api/core/legacy_simulator/tasks.py +19 -0
  56. opentrons/protocol_api/core/module.py +37 -4
  57. opentrons/protocol_api/core/protocol.py +11 -2
  58. opentrons/protocol_api/core/tasks.py +31 -0
  59. opentrons/protocol_api/core/well.py +4 -0
  60. opentrons/protocol_api/labware.py +5 -0
  61. opentrons/protocol_api/module_contexts.py +117 -11
  62. opentrons/protocol_api/protocol_context.py +26 -4
  63. opentrons/protocol_api/robot_context.py +38 -21
  64. opentrons/protocol_api/tasks.py +48 -0
  65. opentrons/protocol_api/validation.py +6 -1
  66. opentrons/protocol_engine/actions/__init__.py +4 -2
  67. opentrons/protocol_engine/actions/actions.py +22 -9
  68. opentrons/protocol_engine/clients/sync_client.py +42 -7
  69. opentrons/protocol_engine/commands/__init__.py +42 -0
  70. opentrons/protocol_engine/commands/absorbance_reader/close_lid.py +2 -15
  71. opentrons/protocol_engine/commands/absorbance_reader/open_lid.py +2 -15
  72. opentrons/protocol_engine/commands/aspirate.py +1 -0
  73. opentrons/protocol_engine/commands/command.py +1 -0
  74. opentrons/protocol_engine/commands/command_unions.py +49 -0
  75. opentrons/protocol_engine/commands/create_timer.py +83 -0
  76. opentrons/protocol_engine/commands/dispense.py +1 -0
  77. opentrons/protocol_engine/commands/drop_tip.py +32 -8
  78. opentrons/protocol_engine/commands/heater_shaker/__init__.py +14 -0
  79. opentrons/protocol_engine/commands/heater_shaker/common.py +20 -0
  80. opentrons/protocol_engine/commands/heater_shaker/set_and_wait_for_shake_speed.py +5 -4
  81. opentrons/protocol_engine/commands/heater_shaker/set_shake_speed.py +136 -0
  82. opentrons/protocol_engine/commands/heater_shaker/set_target_temperature.py +31 -5
  83. opentrons/protocol_engine/commands/movement_common.py +2 -0
  84. opentrons/protocol_engine/commands/pick_up_tip.py +21 -11
  85. opentrons/protocol_engine/commands/set_tip_state.py +97 -0
  86. opentrons/protocol_engine/commands/temperature_module/set_target_temperature.py +38 -7
  87. opentrons/protocol_engine/commands/thermocycler/__init__.py +16 -0
  88. opentrons/protocol_engine/commands/thermocycler/run_extended_profile.py +6 -0
  89. opentrons/protocol_engine/commands/thermocycler/run_profile.py +8 -0
  90. opentrons/protocol_engine/commands/thermocycler/set_target_block_temperature.py +40 -6
  91. opentrons/protocol_engine/commands/thermocycler/set_target_lid_temperature.py +29 -5
  92. opentrons/protocol_engine/commands/thermocycler/start_run_extended_profile.py +191 -0
  93. opentrons/protocol_engine/commands/touch_tip.py +1 -1
  94. opentrons/protocol_engine/commands/unsafe/unsafe_place_labware.py +6 -22
  95. opentrons/protocol_engine/commands/wait_for_tasks.py +98 -0
  96. opentrons/protocol_engine/errors/__init__.py +4 -0
  97. opentrons/protocol_engine/errors/exceptions.py +55 -0
  98. opentrons/protocol_engine/execution/__init__.py +2 -0
  99. opentrons/protocol_engine/execution/command_executor.py +8 -0
  100. opentrons/protocol_engine/execution/create_queue_worker.py +5 -1
  101. opentrons/protocol_engine/execution/labware_movement.py +9 -12
  102. opentrons/protocol_engine/execution/movement.py +2 -0
  103. opentrons/protocol_engine/execution/queue_worker.py +4 -0
  104. opentrons/protocol_engine/execution/run_control.py +8 -0
  105. opentrons/protocol_engine/execution/task_handler.py +157 -0
  106. opentrons/protocol_engine/protocol_engine.py +75 -34
  107. opentrons/protocol_engine/resources/__init__.py +2 -0
  108. opentrons/protocol_engine/resources/concurrency_provider.py +27 -0
  109. opentrons/protocol_engine/resources/deck_configuration_provider.py +7 -0
  110. opentrons/protocol_engine/resources/labware_validation.py +10 -6
  111. opentrons/protocol_engine/state/_well_math.py +60 -18
  112. opentrons/protocol_engine/state/addressable_areas.py +2 -0
  113. opentrons/protocol_engine/state/commands.py +14 -11
  114. opentrons/protocol_engine/state/geometry.py +213 -374
  115. opentrons/protocol_engine/state/labware.py +52 -102
  116. opentrons/protocol_engine/state/labware_origin_math/errors.py +94 -0
  117. opentrons/protocol_engine/state/labware_origin_math/stackup_origin_to_labware_origin.py +1331 -0
  118. opentrons/protocol_engine/state/module_substates/thermocycler_module_substate.py +37 -0
  119. opentrons/protocol_engine/state/modules.py +21 -8
  120. opentrons/protocol_engine/state/motion.py +44 -0
  121. opentrons/protocol_engine/state/state.py +14 -0
  122. opentrons/protocol_engine/state/state_summary.py +2 -0
  123. opentrons/protocol_engine/state/tasks.py +139 -0
  124. opentrons/protocol_engine/state/tips.py +177 -258
  125. opentrons/protocol_engine/state/update_types.py +16 -9
  126. opentrons/protocol_engine/types/__init__.py +9 -3
  127. opentrons/protocol_engine/types/deck_configuration.py +5 -1
  128. opentrons/protocol_engine/types/instrument.py +8 -1
  129. opentrons/protocol_engine/types/labware.py +1 -13
  130. opentrons/protocol_engine/types/module.py +10 -0
  131. opentrons/protocol_engine/types/tasks.py +38 -0
  132. opentrons/protocol_engine/types/tip.py +9 -0
  133. opentrons/protocol_runner/create_simulating_orchestrator.py +29 -2
  134. opentrons/protocol_runner/run_orchestrator.py +18 -2
  135. opentrons/protocols/api_support/definitions.py +1 -1
  136. opentrons/protocols/api_support/types.py +2 -1
  137. opentrons/simulate.py +48 -15
  138. opentrons/system/camera.py +1 -1
  139. {opentrons-8.7.0a6.dist-info → opentrons-8.7.0a7.dist-info}/METADATA +4 -4
  140. {opentrons-8.7.0a6.dist-info → opentrons-8.7.0a7.dist-info}/RECORD +143 -127
  141. opentrons/protocol_engine/state/_labware_origin_math.py +0 -636
  142. {opentrons-8.7.0a6.dist-info → opentrons-8.7.0a7.dist-info}/WHEEL +0 -0
  143. {opentrons-8.7.0a6.dist-info → opentrons-8.7.0a7.dist-info}/entry_points.txt +0 -0
  144. {opentrons-8.7.0a6.dist-info → opentrons-8.7.0a7.dist-info}/licenses/LICENSE +0 -0
@@ -56,7 +56,6 @@ from ..types import (
56
56
  LoadedLabware,
57
57
  ModuleLocation,
58
58
  OverlapOffset,
59
- LabwareMovementOffsetData,
60
59
  OnDeckLabwareLocation,
61
60
  OFF_DECK_LOCATION,
62
61
  )
@@ -401,7 +400,7 @@ class LabwareView:
401
400
  return self._state.labware_by_id[labware_id]
402
401
  except KeyError as e:
403
402
  raise errors.LabwareNotLoadedError(
404
- f"Labware {labware_id} not found."
403
+ f"Labware with id {labware_id} not found."
405
404
  ) from e
406
405
 
407
406
  def known(self, labware_id: str) -> bool:
@@ -430,7 +429,7 @@ class LabwareView:
430
429
  ):
431
430
  return labware.id
432
431
  raise errors.exceptions.LabwareNotLoadedOnLabwareError(
433
- f"There is not labware loaded onto labware {labware_id}"
432
+ f"There is not labware loaded onto labware {self.get_display_name(labware_id)}"
434
433
  )
435
434
 
436
435
  def raise_if_labware_has_non_lid_labware_on_top(self, labware_id: str) -> None:
@@ -443,7 +442,8 @@ class LabwareView:
443
442
  and candidate_id != lid_id
444
443
  ):
445
444
  raise errors.LabwareIsInStackError(
446
- f"Cannot access labware {labware_id} because it has a non-lid labware stacked on top."
445
+ f"Cannot access labware {self.get_display_name(labware_id)} because it has"
446
+ " a non-lid labware stacked on top."
447
447
  )
448
448
 
449
449
  def raise_if_labware_has_labware_on_top(self, labware_id: str) -> None:
@@ -454,9 +454,29 @@ class LabwareView:
454
454
  and labware.location.labwareId == labware_id
455
455
  ):
456
456
  raise errors.LabwareIsInStackError(
457
- f"Cannot access labware {labware_id} because it has another labware stacked on top."
457
+ f"Cannot access labware {self.get_display_name(labware_id)} because it has"
458
+ " another labware stacked on top."
458
459
  )
459
460
 
461
+ def raise_if_not_tip_rack(self, labware_id: str) -> None:
462
+ """Raise if a labware is not a tip rack."""
463
+ if not self.is_tiprack(labware_id):
464
+ raise errors.LabwareIsNotTipRackError(
465
+ f"Labware {self.get_display_name(labware_id)} is not a tip rack and cannot have its well states set."
466
+ )
467
+
468
+ def raise_if_wells_are_invalid(
469
+ self, labware_id: str, well_names: List[str]
470
+ ) -> None:
471
+ """Raise if given wells do not exist with the given labware ID."""
472
+ non_existent_wells = set(well_names) - set(
473
+ self.get_definition(labware_id).wells
474
+ )
475
+ if non_existent_wells:
476
+ raise errors.WellDoesNotExistError(
477
+ f"Tip rack {self.get_display_name(labware_id)} does not have wells: {', '.join(non_existent_wells)}"
478
+ )
479
+
460
480
  def get_by_slot(
461
481
  self,
462
482
  slot_name: Union[DeckSlotName, StagingSlotName],
@@ -663,6 +683,14 @@ class LabwareView:
663
683
  or len(self.get_definition(labware_id).wells) >= 96
664
684
  )
665
685
 
686
+ def get_has_96_subwells(self, labware_id: str) -> bool:
687
+ """True if a labware is a reservoir with a 96-grid of sub-wells."""
688
+ return self.get_has_quirk(labware_id, "offsetPipetteFor96GridSubwells")
689
+
690
+ def get_has_12_subwells(self, labware_id: str) -> bool:
691
+ """True if a labware is a reservoir with a 12-grid of sub-wells."""
692
+ return self.get_has_quirk(labware_id, "offsetPipetteFor12GridSubwells")
693
+
666
694
  def get_well_definition(
667
695
  self,
668
696
  labware_id: str,
@@ -681,7 +709,7 @@ class LabwareView:
681
709
  return definition.wells[well_name]
682
710
  except KeyError as e:
683
711
  raise errors.WellDoesNotExistError(
684
- f"{well_name} does not exist in {labware_id}."
712
+ f"{well_name} does not exist in {self.get_display_name(labware_id)}."
685
713
  ) from e
686
714
 
687
715
  def get_well_geometry(
@@ -691,19 +719,21 @@ class LabwareView:
691
719
  labware_def = self.get_definition(labware_id)
692
720
  if labware_def.innerLabwareGeometry is None:
693
721
  raise errors.IncompleteLabwareDefinitionError(
694
- message=f"No innerLabwareGeometry found in labware definition for labware_id: {labware_id}."
722
+ message=f"No innerLabwareGeometry found in labware definition for {self.get_display_name(labware_id)}."
695
723
  )
696
724
  well_def = self.get_well_definition(labware_id, well_name)
697
725
  geometry_id = well_def.geometryDefinitionId
698
726
  if geometry_id is None:
699
727
  raise errors.IncompleteWellDefinitionError(
700
- message=f"No geometryDefinitionId found in well definition for well: {well_name} in labware_id: {labware_id}"
728
+ message=f"No geometryDefinitionId found in well definition for well {well_name}"
729
+ f" for {self.get_display_name(labware_id)}"
701
730
  )
702
731
  else:
703
732
  well_geometry = labware_def.innerLabwareGeometry.get(geometry_id)
704
733
  if well_geometry is None:
705
734
  raise errors.IncompleteLabwareDefinitionError(
706
- message=f"No innerLabwareGeometry found in labware definition for well_id: {geometry_id} in labware_id: {labware_id}"
735
+ message=f"No innerLabwareGeometry found in labware definition for geometry id {geometry_id}"
736
+ f" for {self.get_display_name(labware_id)}"
707
737
  )
708
738
  return well_geometry
709
739
 
@@ -772,15 +802,15 @@ class LabwareView:
772
802
  contains_wells = all(well_name in labware_wells for well_name in iter(wells))
773
803
  if labware_definition.parameters.isTiprack:
774
804
  raise errors.LabwareIsTipRackError(
775
- f"Given labware: {labware_id} is a tiprack. Can not load liquid."
805
+ f"Given labware {self.get_display_name(labware_id)} is a tip rack. Can not load liquid."
776
806
  )
777
807
  if LabwareRole.adapter in labware_definition.allowedRoles:
778
808
  raise errors.LabwareIsAdapterError(
779
- f"Given labware: {labware_id} is an adapter. Can not load liquid."
809
+ f"Given labware {self.get_display_name(labware_id)} is an adapter. Can not load liquid."
780
810
  )
781
811
  if not contains_wells:
782
812
  raise errors.WellDoesNotExistError(
783
- f"Some of the supplied wells do not match the labwareId: {labware_id}."
813
+ f"Some of the supplied wells do not match the labware {self.get_display_name(labware_id)}."
784
814
  )
785
815
  return list(wells)
786
816
 
@@ -789,7 +819,7 @@ class LabwareView:
789
819
  definition = self.get_definition(labware_id)
790
820
  if definition.parameters.tipLength is None:
791
821
  raise errors.LabwareIsNotTipRackError(
792
- f"Labware {labware_id} has no tip length defined."
822
+ f"Labware {self.get_display_name(labware_id)} has no tip length defined."
793
823
  )
794
824
 
795
825
  return definition.parameters.tipLength - overlap
@@ -1095,7 +1125,9 @@ class LabwareView:
1095
1125
  raise errors.LabwareCannotBeStackedError(
1096
1126
  f"Labware {lid_labware_definition.parameters.loadName} cannot be used as a lid in the Flex Stacker."
1097
1127
  )
1098
- if not labware_validation.validate_labware_can_be_stacked(
1128
+ if isinstance(
1129
+ lid_labware_definition, LabwareDefinition2
1130
+ ) and not labware_validation.validate_legacy_labware_can_be_stacked(
1099
1131
  lid_labware_definition, primary_labware_definition.parameters.loadName
1100
1132
  ):
1101
1133
  raise errors.LabwareCannotBeStackedError(
@@ -1108,7 +1140,9 @@ class LabwareView:
1108
1140
  raise errors.LabwareCannotBeStackedError(
1109
1141
  f"Labware {adapter_labware_definition.parameters.loadName} cannot be used as an adapter in the Flex Stacker."
1110
1142
  )
1111
- if not labware_validation.validate_labware_can_be_stacked(
1143
+ if isinstance(
1144
+ primary_labware_definition, LabwareDefinition2
1145
+ ) and not labware_validation.validate_legacy_labware_can_be_stacked(
1112
1146
  primary_labware_definition,
1113
1147
  adapter_labware_definition.parameters.loadName,
1114
1148
  ):
@@ -1162,9 +1196,9 @@ class LabwareView:
1162
1196
  below_labware = self.get(bottom_labware_id)
1163
1197
  if isinstance(
1164
1198
  top_labware_definition, LabwareDefinition2
1165
- ) and not labware_validation.validate_labware_can_be_stacked(
1166
- top_labware_definition=top_labware_definition,
1167
- below_labware_load_name=below_labware.loadName,
1199
+ ) and not labware_validation.validate_legacy_labware_can_be_stacked(
1200
+ child_labware_definition=top_labware_definition,
1201
+ parent_labware_load_name=below_labware.loadName,
1168
1202
  ):
1169
1203
  raise errors.LabwareCannotBeStackedError(
1170
1204
  f"Labware {top_labware_definition.parameters.loadName} cannot be loaded onto labware {below_labware.loadName}"
@@ -1225,28 +1259,6 @@ class LabwareView:
1225
1259
  uri = self.get_uri_from_definition(self.get_definition(labware_id))
1226
1260
  return uri in _MAGDECK_HALF_MM_LABWARE
1227
1261
 
1228
- def get_deck_default_gripper_offsets(self) -> Optional[LabwareMovementOffsetData]:
1229
- """Get the deck's default gripper offsets."""
1230
- parsed_offsets = (
1231
- self.get_deck_definition().get("gripperOffsets", {}).get("default")
1232
- )
1233
- return (
1234
- LabwareMovementOffsetData(
1235
- pickUpOffset=LabwareOffsetVector(
1236
- x=parsed_offsets["pickUpOffset"]["x"],
1237
- y=parsed_offsets["pickUpOffset"]["y"],
1238
- z=parsed_offsets["pickUpOffset"]["z"],
1239
- ),
1240
- dropOffset=LabwareOffsetVector(
1241
- x=parsed_offsets["dropOffset"]["x"],
1242
- y=parsed_offsets["dropOffset"]["y"],
1243
- z=parsed_offsets["dropOffset"]["z"],
1244
- ),
1245
- )
1246
- if parsed_offsets
1247
- else None
1248
- )
1249
-
1250
1262
  def get_absorbance_reader_lid_definition(self) -> LabwareDefinition:
1251
1263
  """Return the special labware definition for the plate reader lid.
1252
1264
 
@@ -1257,68 +1269,6 @@ class LabwareView:
1257
1269
  "opentrons/opentrons_flex_lid_absorbance_plate_reader_module/1"
1258
1270
  ]
1259
1271
 
1260
- @overload
1261
- def get_child_gripper_offsets(
1262
- self,
1263
- *,
1264
- labware_definition: LabwareDefinition,
1265
- slot_name: Optional[DeckSlotName],
1266
- ) -> Optional[LabwareMovementOffsetData]:
1267
- pass
1268
-
1269
- @overload
1270
- def get_child_gripper_offsets(
1271
- self, *, labware_id: str, slot_name: Optional[DeckSlotName]
1272
- ) -> Optional[LabwareMovementOffsetData]:
1273
- pass
1274
-
1275
- def get_child_gripper_offsets(
1276
- self,
1277
- *,
1278
- labware_definition: Optional[LabwareDefinition] = None,
1279
- labware_id: Optional[str] = None,
1280
- slot_name: Optional[DeckSlotName],
1281
- ) -> Optional[LabwareMovementOffsetData]:
1282
- """Get the grip offsets that a labware says should be applied to children stacked atop it.
1283
-
1284
- Params:
1285
- labware_id: The ID of a parent labware (atop which another labware, the child, will be stacked).
1286
- slot_name: The ancestor slot that the parent labware is ultimately loaded into,
1287
- perhaps after going through a module in the middle.
1288
-
1289
- Returns:
1290
- If `slot_name` is provided, returns the gripper offsets that the parent labware definition
1291
- specifies just for that slot, or `None` if the labware definition doesn't have an
1292
- exact match.
1293
-
1294
- If `slot_name` is `None`, returns the gripper offsets that the parent labware
1295
- definition designates as "default," or `None` if it doesn't designate any as such.
1296
- """
1297
- if labware_id is not None:
1298
- labware_definition = self.get_definition(labware_id)
1299
- else:
1300
- # Should be ensured by our @overloads.
1301
- assert labware_definition is not None
1302
-
1303
- parsed_offsets = labware_definition.gripperOffsets
1304
- offset_key = slot_name.id if slot_name else "default"
1305
-
1306
- if parsed_offsets is None or offset_key not in parsed_offsets:
1307
- return None
1308
- else:
1309
- return LabwareMovementOffsetData(
1310
- pickUpOffset=LabwareOffsetVector.model_construct(
1311
- x=parsed_offsets[offset_key].pickUpOffset.x,
1312
- y=parsed_offsets[offset_key].pickUpOffset.y,
1313
- z=parsed_offsets[offset_key].pickUpOffset.z,
1314
- ),
1315
- dropOffset=LabwareOffsetVector.model_construct(
1316
- x=parsed_offsets[offset_key].dropOffset.x,
1317
- y=parsed_offsets[offset_key].dropOffset.y,
1318
- z=parsed_offsets[offset_key].dropOffset.z,
1319
- ),
1320
- )
1321
-
1322
1272
  def get_grip_force(self, labware_definition: LabwareDefinition) -> float:
1323
1273
  """Get the recommended grip force for gripping labware using gripper."""
1324
1274
  recommended_force = labware_definition.gripForce
@@ -0,0 +1,94 @@
1
+ """Labware origin math errors."""
2
+
3
+ from typing import Any, Dict, Optional, Sequence
4
+
5
+ from opentrons.protocol_engine.errors import ProtocolEngineError
6
+ from opentrons_shared_data.errors import ErrorCodes
7
+ from opentrons_shared_data.errors.exceptions import EnumeratedError
8
+
9
+
10
+ class LabwareLocatingFeatureError(ProtocolEngineError):
11
+ """Base class for errors related to labware locating features."""
12
+
13
+ def __init__(
14
+ self,
15
+ message: Optional[str] = None,
16
+ details: Optional[Dict[str, Any]] = None,
17
+ wrapping: Optional[Sequence[EnumeratedError]] = None,
18
+ ) -> None:
19
+ """Build a LabwareLocatingFeatureError."""
20
+ super().__init__(
21
+ ErrorCodes.LABWARE_LOCATING_FEATURE_ERROR, message, details, wrapping
22
+ )
23
+
24
+
25
+ class MissingLocatingFeatureError(LabwareLocatingFeatureError):
26
+ """Raised when a labware definition is missing a required locating feature."""
27
+
28
+ def __init__(
29
+ self,
30
+ labware_name: str,
31
+ required_feature: str,
32
+ message: Optional[str] = None,
33
+ details: Optional[Dict[str, Any]] = None,
34
+ wrapping: Optional[Sequence[EnumeratedError]] = None,
35
+ ) -> None:
36
+ """Build a MissingLocatingFeatureError."""
37
+ if message is None:
38
+ message = f"Expected {labware_name} to have {required_feature} feature"
39
+
40
+ if details is None:
41
+ details = {
42
+ "labware_name": labware_name,
43
+ "required_feature": required_feature,
44
+ }
45
+
46
+ super().__init__(message, details, wrapping)
47
+
48
+
49
+ class InvalidLabwarePlacementError(LabwareLocatingFeatureError):
50
+ """Raised when a labware cannot be placed in the specified location due to locating feature constraints."""
51
+
52
+ def __init__(
53
+ self,
54
+ feature_name: str,
55
+ invalid_placement: str,
56
+ message: Optional[str] = None,
57
+ details: Optional[Dict[str, Any]] = None,
58
+ wrapping: Optional[Sequence[EnumeratedError]] = None,
59
+ ) -> None:
60
+ """Build an InvalidLabwarePlacementError."""
61
+ if message is None:
62
+ message = f"{feature_name} feature does not support placement: {invalid_placement}"
63
+
64
+ if details is None:
65
+ details = {
66
+ "feature_name": feature_name,
67
+ "invalid_placement": invalid_placement,
68
+ }
69
+
70
+ super().__init__(message, details, wrapping)
71
+
72
+
73
+ class IncompatibleLocatingFeatureError(LabwareLocatingFeatureError):
74
+ """Raised when parent and child labware have incompatible locating features."""
75
+
76
+ def __init__(
77
+ self,
78
+ parent_feature: str,
79
+ child_feature: str,
80
+ message: Optional[str] = None,
81
+ details: Optional[Dict[str, Any]] = None,
82
+ wrapping: Optional[Sequence[EnumeratedError]] = None,
83
+ ) -> None:
84
+ """Build an IncompatibleLocatingFeatureError."""
85
+ if message is None:
86
+ message = f"Incompatible labware features: parent {parent_feature}, child {child_feature}"
87
+
88
+ if details is None:
89
+ details = {
90
+ "parent_feature": parent_feature,
91
+ "child_feature": child_feature,
92
+ }
93
+
94
+ super().__init__(message, details, wrapping)