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
@@ -10,6 +10,7 @@ from ...errors.error_occurrence import ErrorOccurrence
10
10
  from ...errors import CannotPerformModuleAction
11
11
  from opentrons.protocol_engine.types import AddressableAreaLocation
12
12
 
13
+ from opentrons.protocol_engine.resources import labware_validation
13
14
  from ...state.update_types import StateUpdate
14
15
 
15
16
 
@@ -52,35 +53,41 @@ class CloseLidImpl(AbstractCommandImpl[CloseLidParams, SuccessData[CloseLidResul
52
53
 
53
54
  async def execute(self, params: CloseLidParams) -> SuccessData[CloseLidResult]:
54
55
  """Execute the close lid command."""
55
- state_update = StateUpdate()
56
56
  mod_substate = self._state_view.modules.get_absorbance_reader_substate(
57
57
  module_id=params.moduleId
58
58
  )
59
59
 
60
+ # lid should currently be on the module
61
+ assert mod_substate.lid_id is not None
62
+ loaded_lid = self._state_view.labware.get(mod_substate.lid_id)
63
+ assert labware_validation.is_absorbance_reader_lid(loaded_lid.loadName)
64
+
60
65
  hardware_lid_status = AbsorbanceReaderLidStatus.OFF
66
+ # If the lid is closed, if the lid is open No-op out
61
67
  if not self._state_view.config.use_virtual_modules:
62
68
  abs_reader = self._equipment.get_module_hardware_api(mod_substate.module_id)
63
69
 
64
70
  if abs_reader is not None:
65
- hardware_lid_status = await abs_reader.get_current_lid_status()
71
+ result = await abs_reader.get_current_lid_status()
72
+ hardware_lid_status = result
66
73
  else:
67
74
  raise CannotPerformModuleAction(
68
75
  "Could not reach the Hardware API for Opentrons Plate Reader Module."
69
76
  )
70
77
 
78
+ # If the lid is already ON, no-op losing lid
71
79
  if hardware_lid_status is AbsorbanceReaderLidStatus.ON:
72
- # The lid is already physically ON, so we can no-op physically closing it
73
- state_update.set_absorbance_reader_lid(
74
- module_id=mod_substate.module_id, is_lid_on=True
80
+ # The lid is already On, so we can no-op and return the lids current location data
81
+ assert isinstance(loaded_lid.location, AddressableAreaLocation)
82
+ new_location = loaded_lid.location
83
+ new_offset_id = self._equipment.find_applicable_labware_offset_id(
84
+ labware_definition_uri=loaded_lid.definitionUri,
85
+ labware_location=loaded_lid.location,
75
86
  )
76
87
  else:
77
88
  # Allow propagation of ModuleNotAttachedError.
78
89
  _ = self._equipment.get_module_hardware_api(mod_substate.module_id)
79
90
 
80
- lid_definition = (
81
- self._state_view.labware.get_absorbance_reader_lid_definition()
82
- )
83
-
84
91
  current_location = self._state_view.modules.absorbance_reader_dock_location(
85
92
  params.moduleId
86
93
  )
@@ -100,29 +107,35 @@ class CloseLidImpl(AbstractCommandImpl[CloseLidParams, SuccessData[CloseLidResul
100
107
  )
101
108
  )
102
109
 
103
- # The lid's labware definition stores gripper offsets for itself in the
104
- # space normally meant for offsets for labware stacked atop it.
105
- lid_gripper_offsets = self._state_view.labware.get_child_gripper_offsets(
106
- labware_definition=lid_definition,
107
- slot_name=None,
110
+ lid_gripper_offsets = self._state_view.labware.get_labware_gripper_offsets(
111
+ loaded_lid.id, None
108
112
  )
109
113
  if lid_gripper_offsets is None:
110
114
  raise ValueError(
111
115
  "Gripper Offset values for Absorbance Reader Lid labware must not be None."
112
116
  )
113
117
 
118
+ # Skips gripper moves when using virtual gripper
114
119
  await self._labware_movement.move_labware_with_gripper(
115
- labware_definition=lid_definition,
120
+ labware_id=loaded_lid.id,
116
121
  current_location=current_location,
117
122
  new_location=new_location,
118
123
  user_offset_data=lid_gripper_offsets,
119
124
  post_drop_slide_offset=None,
120
125
  )
121
- state_update.set_absorbance_reader_lid(
122
- module_id=mod_substate.module_id,
123
- is_lid_on=True,
126
+
127
+ new_offset_id = self._equipment.find_applicable_labware_offset_id(
128
+ labware_definition_uri=loaded_lid.definitionUri,
129
+ labware_location=new_location,
124
130
  )
125
131
 
132
+ state_update = StateUpdate()
133
+ state_update.set_labware_location(
134
+ labware_id=loaded_lid.id,
135
+ new_location=new_location,
136
+ new_offset_id=new_offset_id,
137
+ )
138
+
126
139
  return SuccessData(
127
140
  public=CloseLidResult(),
128
141
  state_update=state_update,
@@ -10,7 +10,6 @@ from opentrons.protocol_engine.types import ABSMeasureMode
10
10
 
11
11
  from ..command import AbstractCommandImpl, BaseCommand, BaseCommandCreate, SuccessData
12
12
  from ...errors.error_occurrence import ErrorOccurrence
13
- from ...errors import InvalidWavelengthError
14
13
 
15
14
  if TYPE_CHECKING:
16
15
  from opentrons.protocol_engine.state.state import StateView
@@ -70,41 +69,30 @@ class InitializeImpl(
70
69
  unsupported_wavelengths = sample_wavelengths.difference(
71
70
  supported_wavelengths
72
71
  )
73
- sample_wl_str = ", ".join([str(w) + "nm" for w in sample_wavelengths])
74
- supported_wl_str = ", ".join([str(w) + "nm" for w in supported_wavelengths])
75
- unsupported_wl_str = ", ".join(
76
- [str(w) + "nm" for w in unsupported_wavelengths]
77
- )
78
72
  if unsupported_wavelengths:
79
- raise InvalidWavelengthError(
80
- f"Unsupported wavelengths: {unsupported_wl_str}. "
81
- f" Use one of {supported_wl_str} instead."
82
- )
73
+ raise ValueError(f"Unsupported wavelengths: {unsupported_wavelengths}")
83
74
 
84
75
  if params.measureMode == "single":
85
76
  if sample_wavelengths_len != 1:
86
77
  raise ValueError(
87
- f"Measure mode `single` requires one sample wavelength,"
88
- f" {sample_wl_str} provided instead."
78
+ f"single requires one sample wavelength, provided {sample_wavelengths}"
89
79
  )
90
80
  if (
91
81
  reference_wavelength is not None
92
82
  and reference_wavelength not in supported_wavelengths
93
83
  ):
94
- raise InvalidWavelengthError(
95
- f"Reference wavelength {reference_wavelength}nm is not supported."
96
- f" Use one of {supported_wl_str} instead."
84
+ raise ValueError(
85
+ f"Reference wavelength {reference_wavelength} not supported {supported_wavelengths}"
97
86
  )
98
87
 
99
88
  if params.measureMode == "multi":
100
89
  if sample_wavelengths_len < 1 or sample_wavelengths_len > 6:
101
90
  raise ValueError(
102
- f"Measure mode `multi` requires 1-6 sample wavelengths,"
103
- f" {sample_wl_str} provided instead."
91
+ f"multi requires 1-6 sample wavelengths, provided {sample_wavelengths}"
104
92
  )
105
93
  if reference_wavelength is not None:
106
- raise ValueError(
107
- "Reference wavelength cannot be used with Measure mode `multi`."
94
+ raise RuntimeError(
95
+ "Reference wavelength cannot be used with multi mode."
108
96
  )
109
97
 
110
98
  await abs_reader.set_sample_wavelength(
@@ -9,6 +9,7 @@ from ..command import AbstractCommandImpl, BaseCommand, BaseCommandCreate, Succe
9
9
  from ...errors.error_occurrence import ErrorOccurrence
10
10
  from ...errors import CannotPerformModuleAction
11
11
 
12
+ from opentrons.protocol_engine.resources import labware_validation
12
13
  from opentrons.protocol_engine.types import AddressableAreaLocation
13
14
 
14
15
  from opentrons.drivers.types import AbsorbanceReaderLidStatus
@@ -53,35 +54,39 @@ class OpenLidImpl(AbstractCommandImpl[OpenLidParams, SuccessData[OpenLidResult]]
53
54
 
54
55
  async def execute(self, params: OpenLidParams) -> SuccessData[OpenLidResult]:
55
56
  """Move the absorbance reader lid from the module to the lid dock."""
56
- state_update = StateUpdate()
57
57
  mod_substate = self._state_view.modules.get_absorbance_reader_substate(
58
58
  module_id=params.moduleId
59
59
  )
60
+ # lid should currently be on the module
61
+ assert mod_substate.lid_id is not None
62
+ loaded_lid = self._state_view.labware.get(mod_substate.lid_id)
63
+ assert labware_validation.is_absorbance_reader_lid(loaded_lid.loadName)
60
64
 
61
65
  hardware_lid_status = AbsorbanceReaderLidStatus.ON
66
+ # If the lid is closed, if the lid is open No-op out
62
67
  if not self._state_view.config.use_virtual_modules:
63
68
  abs_reader = self._equipment.get_module_hardware_api(mod_substate.module_id)
64
69
 
65
70
  if abs_reader is not None:
66
- hardware_lid_status = await abs_reader.get_current_lid_status()
71
+ result = await abs_reader.get_current_lid_status()
72
+ hardware_lid_status = result
67
73
  else:
68
74
  raise CannotPerformModuleAction(
69
75
  "Could not reach the Hardware API for Opentrons Plate Reader Module."
70
76
  )
71
77
 
78
+ # If the lid is already OFF, no-op the lid removal
72
79
  if hardware_lid_status is AbsorbanceReaderLidStatus.OFF:
73
- # The lid is already physically OFF, so we can no-op physically closing it
74
- state_update.set_absorbance_reader_lid(
75
- module_id=mod_substate.module_id, is_lid_on=False
80
+ assert isinstance(loaded_lid.location, AddressableAreaLocation)
81
+ new_location = loaded_lid.location
82
+ new_offset_id = self._equipment.find_applicable_labware_offset_id(
83
+ labware_definition_uri=loaded_lid.definitionUri,
84
+ labware_location=loaded_lid.location,
76
85
  )
77
86
  else:
78
87
  # Allow propagation of ModuleNotAttachedError.
79
88
  _ = self._equipment.get_module_hardware_api(mod_substate.module_id)
80
89
 
81
- lid_definition = (
82
- self._state_view.labware.get_absorbance_reader_lid_definition()
83
- )
84
-
85
90
  absorbance_model = self._state_view.modules.get_requested_model(
86
91
  params.moduleId
87
92
  )
@@ -101,28 +106,35 @@ class OpenLidImpl(AbstractCommandImpl[OpenLidParams, SuccessData[OpenLidResult]]
101
106
  mod_substate.module_id
102
107
  )
103
108
 
104
- # The lid's labware definition stores gripper offsets for itself in the
105
- # space normally meant for offsets for labware stacked atop it.
106
- lid_gripper_offsets = self._state_view.labware.get_child_gripper_offsets(
107
- labware_definition=lid_definition,
108
- slot_name=None,
109
+ lid_gripper_offsets = self._state_view.labware.get_labware_gripper_offsets(
110
+ loaded_lid.id, None
109
111
  )
110
112
  if lid_gripper_offsets is None:
111
113
  raise ValueError(
112
114
  "Gripper Offset values for Absorbance Reader Lid labware must not be None."
113
115
  )
114
116
 
117
+ # Skips gripper moves when using virtual gripper
115
118
  await self._labware_movement.move_labware_with_gripper(
116
- labware_definition=lid_definition,
119
+ labware_id=loaded_lid.id,
117
120
  current_location=current_location,
118
121
  new_location=new_location,
119
122
  user_offset_data=lid_gripper_offsets,
120
123
  post_drop_slide_offset=None,
121
124
  )
122
- state_update.set_absorbance_reader_lid(
123
- module_id=mod_substate.module_id, is_lid_on=False
125
+ new_offset_id = self._equipment.find_applicable_labware_offset_id(
126
+ labware_definition_uri=loaded_lid.definitionUri,
127
+ labware_location=new_location,
124
128
  )
125
129
 
130
+ state_update = StateUpdate()
131
+
132
+ state_update.set_labware_location(
133
+ labware_id=loaded_lid.id,
134
+ new_location=new_location,
135
+ new_offset_id=new_offset_id,
136
+ )
137
+
126
138
  return SuccessData(
127
139
  public=OpenLidResult(),
128
140
  state_update=state_update,
@@ -80,10 +80,6 @@ class ReadAbsorbanceImpl(
80
80
  raise CannotPerformModuleAction(
81
81
  "Cannot perform Read action on Absorbance Reader without calling `.initialize(...)` first."
82
82
  )
83
- if abs_reader_substate.is_lid_on is False:
84
- raise CannotPerformModuleAction(
85
- "Absorbance Plate Reader can't read a plate with the lid open. Call `close_lid()` first."
86
- )
87
83
 
88
84
  # TODO: we need to return a file ID and increase the file count even when a moduel is not attached
89
85
  if (
@@ -83,11 +83,10 @@ class AspirateInPlaceImplementation(
83
83
  TipNotAttachedError: if no tip is attached to the pipette.
84
84
  PipetteNotReadyToAspirateError: pipette plunger is not ready.
85
85
  """
86
- state_update = StateUpdate()
87
-
88
86
  ready_to_aspirate = self._pipetting.get_is_ready_to_aspirate(
89
87
  pipette_id=params.pipetteId,
90
88
  )
89
+
91
90
  if not ready_to_aspirate:
92
91
  raise PipetteNotReadyToAspirateError(
93
92
  "Pipette cannot aspirate in place because of a previous blow out."
@@ -95,10 +94,11 @@ class AspirateInPlaceImplementation(
95
94
  " so the plunger can be reset in a known safe position."
96
95
  )
97
96
 
97
+ state_update = StateUpdate()
98
98
  current_location = self._state_view.pipettes.get_current_location()
99
- current_position = await self._gantry_mover.get_position(params.pipetteId)
100
99
 
101
100
  try:
101
+ current_position = await self._gantry_mover.get_position(params.pipetteId)
102
102
  volume = await self._pipetting.aspirate_in_place(
103
103
  pipette_id=params.pipetteId,
104
104
  volume=params.volume,
@@ -185,9 +185,7 @@ class BaseCommand(
185
185
  )
186
186
  error: Union[
187
187
  _ErrorT,
188
- # ErrorOccurrence here is a catch-all for undefined errors not captured by
189
- # _ErrorT, or defined errors that don't parse into _ErrorT because, for example,
190
- # they are from an older software version that was missing some fields.
188
+ # ErrorOccurrence here is for undefined errors not captured by _ErrorT.
191
189
  ErrorOccurrence,
192
190
  None,
193
191
  ] = Field(
@@ -76,8 +76,8 @@ class DispenseInPlaceImplementation(
76
76
  """Dispense without moving the pipette."""
77
77
  state_update = StateUpdate()
78
78
  current_location = self._state_view.pipettes.get_current_location()
79
- current_position = await self._gantry_mover.get_position(params.pipetteId)
80
79
  try:
80
+ current_position = await self._gantry_mover.get_position(params.pipetteId)
81
81
  volume = await self._pipetting.dispense_in_place(
82
82
  pipette_id=params.pipetteId,
83
83
  volume=params.volume,
@@ -145,7 +145,6 @@ class DropTipImplementation(AbstractCommandImpl[DropTipParams, _ExecuteReturn]):
145
145
  error=exception,
146
146
  )
147
147
  ],
148
- errorInfo={"retryLocation": position},
149
148
  )
150
149
  state_update_if_false_positive = update_types.StateUpdate()
151
150
  state_update_if_false_positive.update_pipette_tip_state(
@@ -166,7 +165,7 @@ class DropTipImplementation(AbstractCommandImpl[DropTipParams, _ExecuteReturn]):
166
165
  )
167
166
 
168
167
 
169
- class DropTip(BaseCommand[DropTipParams, DropTipResult, TipPhysicallyAttachedError]):
168
+ class DropTip(BaseCommand[DropTipParams, DropTipResult, ErrorOccurrence]):
170
169
  """Drop tip command model."""
171
170
 
172
171
  commandType: DropTipCommandType = "dropTip"
@@ -18,7 +18,7 @@ from ..resources.model_utils import ModelUtils
18
18
  from ..state import update_types
19
19
 
20
20
  if TYPE_CHECKING:
21
- from ..execution import TipHandler, GantryMover
21
+ from ..execution import TipHandler
22
22
 
23
23
 
24
24
  DropTipInPlaceCommandType = Literal["dropTipInPlace"]
@@ -57,19 +57,15 @@ class DropTipInPlaceImplementation(
57
57
  self,
58
58
  tip_handler: TipHandler,
59
59
  model_utils: ModelUtils,
60
- gantry_mover: GantryMover,
61
60
  **kwargs: object,
62
61
  ) -> None:
63
62
  self._tip_handler = tip_handler
64
63
  self._model_utils = model_utils
65
- self._gantry_mover = gantry_mover
66
64
 
67
65
  async def execute(self, params: DropTipInPlaceParams) -> _ExecuteReturn:
68
66
  """Drop a tip using the requested pipette."""
69
67
  state_update = update_types.StateUpdate()
70
68
 
71
- retry_location = await self._gantry_mover.get_position(params.pipetteId)
72
-
73
69
  try:
74
70
  await self._tip_handler.drop_tip(
75
71
  pipette_id=params.pipetteId, home_after=params.homeAfter
@@ -89,7 +85,6 @@ class DropTipInPlaceImplementation(
89
85
  error=exception,
90
86
  )
91
87
  ],
92
- errorInfo={"retryLocation": retry_location},
93
88
  )
94
89
  return DefinedErrorData(
95
90
  public=error,
@@ -104,7 +99,7 @@ class DropTipInPlaceImplementation(
104
99
 
105
100
 
106
101
  class DropTipInPlace(
107
- BaseCommand[DropTipInPlaceParams, DropTipInPlaceResult, TipPhysicallyAttachedError]
102
+ BaseCommand[DropTipInPlaceParams, DropTipInPlaceResult, ErrorOccurrence]
108
103
  ):
109
104
  """Drop tip in place command model."""
110
105
 
@@ -10,8 +10,6 @@ from ..errors import LabwareIsNotAllowedInLocationError
10
10
  from ..resources import labware_validation, fixture_validation
11
11
  from ..types import (
12
12
  LabwareLocation,
13
- ModuleLocation,
14
- ModuleModel,
15
13
  OnLabwareLocation,
16
14
  DeckSlotLocation,
17
15
  AddressableAreaLocation,
@@ -162,13 +160,6 @@ class LoadLabwareImplementation(
162
160
  top_labware_definition=loaded_labware.definition,
163
161
  bottom_labware_id=verified_location.labwareId,
164
162
  )
165
- # Validate labware for the absorbance reader
166
- elif isinstance(params.location, ModuleLocation):
167
- module = self._state_view.modules.get(params.location.moduleId)
168
- if module is not None and module.model == ModuleModel.ABSORBANCE_READER_V1:
169
- self._state_view.labware.raise_if_labware_incompatible_with_plate_reader(
170
- loaded_labware.definition
171
- )
172
163
 
173
164
  return SuccessData(
174
165
  public=LoadLabwareResult(
@@ -5,6 +5,7 @@ from typing_extensions import Literal
5
5
  from pydantic import BaseModel, Field
6
6
 
7
7
  from .command import AbstractCommandImpl, BaseCommand, BaseCommandCreate, SuccessData
8
+ from ..errors import ModuleNotLoadedError
8
9
  from ..errors.error_occurrence import ErrorOccurrence
9
10
  from ..types import (
10
11
  DeckSlotLocation,
@@ -16,6 +17,7 @@ from opentrons.types import DeckSlotName
16
17
 
17
18
  from opentrons.protocol_engine.resources import deck_configuration_provider
18
19
 
20
+ from opentrons.drivers.types import AbsorbanceReaderLidStatus
19
21
 
20
22
  if TYPE_CHECKING:
21
23
  from ..state.state import StateView
@@ -150,6 +152,43 @@ class LoadModuleImplementation(
150
152
  module_id=params.moduleId,
151
153
  )
152
154
 
155
+ # Handle lid position update for loaded Plate Reader module on deck
156
+ if (
157
+ not self._state_view.config.use_virtual_modules
158
+ and params.model == ModuleModel.ABSORBANCE_READER_V1
159
+ and params.moduleId is not None
160
+ ):
161
+ try:
162
+ abs_reader = self._equipment.get_module_hardware_api(
163
+ self._state_view.modules.get_absorbance_reader_substate(
164
+ params.moduleId
165
+ ).module_id
166
+ )
167
+ except ModuleNotLoadedError:
168
+ abs_reader = None
169
+
170
+ if abs_reader is not None:
171
+ result = await abs_reader.get_current_lid_status()
172
+ if (
173
+ isinstance(result, AbsorbanceReaderLidStatus)
174
+ and result is not AbsorbanceReaderLidStatus.ON
175
+ ):
176
+ reader_area = self._state_view.modules.ensure_and_convert_module_fixture_location(
177
+ params.location.slotName,
178
+ self._state_view.config.deck_type,
179
+ params.model,
180
+ )
181
+ lid_labware = self._state_view.labware.get_by_addressable_area(
182
+ reader_area
183
+ )
184
+
185
+ if lid_labware is not None:
186
+ self._state_view.labware._state.labware_by_id[
187
+ lid_labware.id
188
+ ].location = self._state_view.modules.absorbance_reader_dock_location(
189
+ params.moduleId
190
+ )
191
+
153
192
  return SuccessData(
154
193
  public=LoadModuleResult(
155
194
  moduleId=loaded_module.module_id,
@@ -13,22 +13,16 @@ from typing_extensions import Literal
13
13
  from opentrons.protocol_engine.resources.model_utils import ModelUtils
14
14
  from opentrons.types import Point
15
15
  from ..types import (
16
- ModuleModel,
17
16
  CurrentWell,
18
17
  LabwareLocation,
19
18
  DeckSlotLocation,
20
- ModuleLocation,
21
19
  OnLabwareLocation,
22
20
  AddressableAreaLocation,
23
21
  LabwareMovementStrategy,
24
22
  LabwareOffsetVector,
25
23
  LabwareMovementOffsetData,
26
24
  )
27
- from ..errors import (
28
- LabwareMovementNotAllowedError,
29
- NotSupportedOnRobotType,
30
- LabwareOffsetDoesNotExistError,
31
- )
25
+ from ..errors import LabwareMovementNotAllowedError, NotSupportedOnRobotType
32
26
  from ..resources import labware_validation, fixture_validation
33
27
  from .command import (
34
28
  AbstractCommandImpl,
@@ -136,7 +130,6 @@ class MoveLabwareImplementation(AbstractCommandImpl[MoveLabwareParams, _ExecuteR
136
130
  )
137
131
  definition_uri = current_labware.definitionUri
138
132
  post_drop_slide_offset: Optional[Point] = None
139
- trash_lid_drop_offset: Optional[LabwareOffsetVector] = None
140
133
 
141
134
  if self._state_view.labware.is_fixed_trash(params.labwareId):
142
135
  raise LabwareMovementNotAllowedError(
@@ -145,11 +138,9 @@ class MoveLabwareImplementation(AbstractCommandImpl[MoveLabwareParams, _ExecuteR
145
138
 
146
139
  if isinstance(params.newLocation, AddressableAreaLocation):
147
140
  area_name = params.newLocation.addressableAreaName
148
- if (
149
- not fixture_validation.is_gripper_waste_chute(area_name)
150
- and not fixture_validation.is_deck_slot(area_name)
151
- and not fixture_validation.is_trash(area_name)
152
- ):
141
+ if not fixture_validation.is_gripper_waste_chute(
142
+ area_name
143
+ ) and not fixture_validation.is_deck_slot(area_name):
153
144
  raise LabwareMovementNotAllowedError(
154
145
  f"Cannot move {current_labware.loadName} to addressable area {area_name}"
155
146
  )
@@ -171,32 +162,6 @@ class MoveLabwareImplementation(AbstractCommandImpl[MoveLabwareParams, _ExecuteR
171
162
  y=0,
172
163
  z=0,
173
164
  )
174
- elif fixture_validation.is_trash(area_name):
175
- # When dropping labware in the trash bins we want to ensure they are lids
176
- # and enforce a y-axis drop offset to ensure they fall within the trash bin
177
- if labware_validation.validate_definition_is_lid(
178
- self._state_view.labware.get_definition(params.labwareId)
179
- ):
180
- lid_disposable_offfets = (
181
- current_labware_definition.gripperOffsets.get(
182
- "lidDisposalOffsets"
183
- )
184
- )
185
- if lid_disposable_offfets is not None:
186
- trash_lid_drop_offset = LabwareOffsetVector(
187
- x=lid_disposable_offfets.dropOffset.x,
188
- y=lid_disposable_offfets.dropOffset.y,
189
- z=lid_disposable_offfets.dropOffset.z,
190
- )
191
- else:
192
- raise LabwareOffsetDoesNotExistError(
193
- f"Labware Definition {current_labware.loadName} does not contain required field 'lidDisposalOffsets' of 'gripperOffsets'."
194
- )
195
- else:
196
- raise LabwareMovementNotAllowedError(
197
- "Can only move labware with allowed role 'Lid' to a Trash Bin."
198
- )
199
-
200
165
  elif isinstance(params.newLocation, DeckSlotLocation):
201
166
  self._state_view.addressable_areas.raise_if_area_not_in_deck_configuration(
202
167
  params.newLocation.slotName.id
@@ -223,13 +188,6 @@ class MoveLabwareImplementation(AbstractCommandImpl[MoveLabwareParams, _ExecuteR
223
188
  raise LabwareMovementNotAllowedError(
224
189
  "Cannot move a labware onto itself."
225
190
  )
226
- # Validate labware for the absorbance reader
227
- elif isinstance(available_new_location, ModuleLocation):
228
- module = self._state_view.modules.get(available_new_location.moduleId)
229
- if module is not None and module.model == ModuleModel.ABSORBANCE_READER_V1:
230
- self._state_view.labware.raise_if_labware_incompatible_with_plate_reader(
231
- current_labware_definition
232
- )
233
191
 
234
192
  # Allow propagation of ModuleNotLoadedError.
235
193
  new_offset_id = self._equipment.find_applicable_labware_offset_id(
@@ -274,9 +232,6 @@ class MoveLabwareImplementation(AbstractCommandImpl[MoveLabwareParams, _ExecuteR
274
232
  dropOffset=params.dropOffset or LabwareOffsetVector(x=0, y=0, z=0),
275
233
  )
276
234
 
277
- if trash_lid_drop_offset:
278
- user_offset_data.dropOffset += trash_lid_drop_offset
279
-
280
235
  try:
281
236
  # Skips gripper moves when using virtual gripper
282
237
  await self._labware_movement.move_labware_with_gripper(
@@ -82,7 +82,7 @@ class TipPhysicallyMissingError(ErrorOccurrence):
82
82
  isDefined: bool = True
83
83
  errorType: Literal["tipPhysicallyMissing"] = "tipPhysicallyMissing"
84
84
  errorCode: str = ErrorCodes.TIP_PICKUP_FAILED.value.code
85
- detail: str = "No Tip Detected"
85
+ detail: str = "No tip detected."
86
86
 
87
87
 
88
88
  _ExecuteReturn = Union[
@@ -148,12 +148,7 @@ class DestinationPositionResult(BaseModel):
148
148
 
149
149
 
150
150
  class ErrorLocationInfo(TypedDict):
151
- """Holds a retry location for in-place error recovery.
152
-
153
- This is appropriate to pass to a `moveToCoordinates` command,
154
- assuming the pipette has not been configured with a different nozzle layout
155
- in the meantime.
156
- """
151
+ """Holds a retry location for in-place error recovery."""
157
152
 
158
153
  retryLocation: Tuple[float, float, float]
159
154
 
@@ -206,5 +201,3 @@ class TipPhysicallyAttachedError(ErrorOccurrence):
206
201
 
207
202
  errorCode: str = ErrorCodes.TIP_DROP_FAILED.value.code
208
203
  detail: str = ErrorCodes.TIP_DROP_FAILED.value.detail
209
-
210
- errorInfo: ErrorLocationInfo