opentrons 8.2.0a1__py2.py3-none-any.whl → 8.2.0a3__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 (28) hide show
  1. opentrons/drivers/absorbance_reader/async_byonoy.py +5 -4
  2. opentrons/hardware_control/backends/ot3utils.py +1 -0
  3. opentrons/hardware_control/modules/absorbance_reader.py +2 -0
  4. opentrons/hardware_control/ot3api.py +5 -5
  5. opentrons/hardware_control/protocols/position_estimator.py +3 -1
  6. opentrons/hardware_control/types.py +2 -0
  7. opentrons/protocol_api/module_contexts.py +56 -26
  8. opentrons/protocol_engine/commands/absorbance_reader/initialize.py +19 -7
  9. opentrons/protocol_engine/commands/aspirate_in_place.py +3 -3
  10. opentrons/protocol_engine/commands/command.py +3 -1
  11. opentrons/protocol_engine/commands/dispense_in_place.py +1 -1
  12. opentrons/protocol_engine/commands/drop_tip.py +2 -1
  13. opentrons/protocol_engine/commands/drop_tip_in_place.py +7 -2
  14. opentrons/protocol_engine/commands/pick_up_tip.py +1 -1
  15. opentrons/protocol_engine/commands/pipetting_common.py +8 -1
  16. opentrons/protocol_engine/commands/unsafe/unsafe_place_labware.py +8 -3
  17. opentrons/protocol_engine/commands/unsafe/update_position_estimators.py +5 -1
  18. opentrons/protocol_engine/errors/__init__.py +2 -0
  19. opentrons/protocol_engine/errors/error_occurrence.py +8 -3
  20. opentrons/protocol_engine/errors/exceptions.py +13 -0
  21. opentrons/protocol_runner/run_orchestrator.py +15 -0
  22. opentrons/protocols/parameters/csv_parameter_interface.py +3 -1
  23. {opentrons-8.2.0a1.dist-info → opentrons-8.2.0a3.dist-info}/METADATA +4 -4
  24. {opentrons-8.2.0a1.dist-info → opentrons-8.2.0a3.dist-info}/RECORD +28 -28
  25. {opentrons-8.2.0a1.dist-info → opentrons-8.2.0a3.dist-info}/LICENSE +0 -0
  26. {opentrons-8.2.0a1.dist-info → opentrons-8.2.0a3.dist-info}/WHEEL +0 -0
  27. {opentrons-8.2.0a1.dist-info → opentrons-8.2.0a3.dist-info}/entry_points.txt +0 -0
  28. {opentrons-8.2.0a1.dist-info → opentrons-8.2.0a3.dist-info}/top_level.txt +0 -0
@@ -23,7 +23,8 @@ from opentrons.hardware_control.modules.errors import AbsorbanceReaderDisconnect
23
23
 
24
24
 
25
25
  SN_PARSER = re.compile(r'ATTRS{serial}=="(?P<serial>.+?)"')
26
- VERSION_PARSER = re.compile(r"Absorbance (?P<version>V\d+\.\d+\.\d+)")
26
+ # match semver V0.0.0 (old format) or one integer (latest format)
27
+ VERSION_PARSER = re.compile(r"(?P<version>(V\d+\.\d+\.\d+|^\d+$))")
27
28
  SERIAL_PARSER = re.compile(r"(?P<serial>(OPT|BYO)[A-Z]{3}[0-9]+)")
28
29
 
29
30
 
@@ -156,10 +157,10 @@ class AsyncByonoy:
156
157
  func=partial(self._interface.get_device_information, handle),
157
158
  )
158
159
  self._raise_if_error(err.name, f"Error getting device information: {err}")
159
- serial_match = SERIAL_PARSER.fullmatch(device_info.sn)
160
- version_match = VERSION_PARSER.match(device_info.version)
160
+ serial_match = SERIAL_PARSER.match(device_info.sn)
161
+ version_match = VERSION_PARSER.search(device_info.version)
161
162
  serial = serial_match["serial"].strip() if serial_match else "OPTMAA00000"
162
- version = version_match["version"].lower() if version_match else "v0.0.0"
163
+ version = version_match["version"].lower() if version_match else "v0"
163
164
  info = {
164
165
  "serial": serial,
165
166
  "version": version,
@@ -652,6 +652,7 @@ _gripper_jaw_state_lookup: Dict[FirmwareGripperjawState, GripperJawState] = {
652
652
  FirmwareGripperjawState.force_controlling_home: GripperJawState.HOMED_READY,
653
653
  FirmwareGripperjawState.force_controlling: GripperJawState.GRIPPING,
654
654
  FirmwareGripperjawState.position_controlling: GripperJawState.HOLDING,
655
+ FirmwareGripperjawState.stopped: GripperJawState.STOPPED,
655
656
  }
656
657
 
657
658
 
@@ -312,6 +312,8 @@ class AbsorbanceReader(mod_abc.AbstractModule):
312
312
  log.debug(f"Updating {self.name}: {self.port} with {firmware_file_path}")
313
313
  self._updating = True
314
314
  success, res = await self._driver.update_firmware(firmware_file_path)
315
+ # it takes time for the plate reader to re-init after an update.
316
+ await asyncio.sleep(10)
315
317
  self._device_info = await self._driver.get_device_info()
316
318
  await self._poller.start()
317
319
  self._updating = False
@@ -775,12 +775,12 @@ class OT3API(
775
775
  """
776
776
  Function to update motor estimation for a set of axes
777
777
  """
778
+ if axes is None:
779
+ axes = [ax for ax in Axis]
778
780
 
779
- if axes:
780
- checked_axes = [ax for ax in axes if ax in Axis]
781
- else:
782
- checked_axes = [ax for ax in Axis]
783
- await self._backend.update_motor_estimation(checked_axes)
781
+ axes = [ax for ax in axes if self._backend.axis_is_present(ax)]
782
+
783
+ await self._backend.update_motor_estimation(axes)
784
784
 
785
785
  # Global actions API
786
786
  def pause(self, pause_type: PauseType) -> None:
@@ -10,7 +10,7 @@ class PositionEstimator(Protocol):
10
10
  """Update the specified axes' position estimators from their encoders.
11
11
 
12
12
  This will allow these axes to make a non-home move even if they do not currently have
13
- a position estimation (unless there is no tracked poition from the encoders, as would be
13
+ a position estimation (unless there is no tracked position from the encoders, as would be
14
14
  true immediately after boot).
15
15
 
16
16
  Axis encoders have less precision than their position estimators. Calling this function will
@@ -19,6 +19,8 @@ class PositionEstimator(Protocol):
19
19
 
20
20
  This function updates only the requested axes. If other axes have bad position estimation,
21
21
  moves that require those axes or attempts to get the position of those axes will still fail.
22
+ Axes that are not currently available (like a plunger for a pipette that is not connected)
23
+ will be ignored.
22
24
  """
23
25
  ...
24
26
 
@@ -625,6 +625,8 @@ class GripperJawState(enum.Enum):
625
625
  #: the gripper is actively force-control gripping something
626
626
  HOLDING = enum.auto()
627
627
  #: the gripper is in position-control mode
628
+ STOPPED = enum.auto()
629
+ #: the gripper has been homed before but is stopped now
628
630
 
629
631
 
630
632
  class InstrumentProbeType(enum.Enum):
@@ -581,7 +581,7 @@ class ThermocyclerContext(ModuleContext):
581
581
  individual well of the loaded labware, in µL.
582
582
  If not specified, the default is 25 µL.
583
583
 
584
- .. note:
584
+ .. note::
585
585
 
586
586
  If ``hold_time_minutes`` and ``hold_time_seconds`` are not
587
587
  specified, the Thermocycler will proceed to the next command
@@ -605,7 +605,7 @@ class ThermocyclerContext(ModuleContext):
605
605
  :param temperature: A value between 37 and 110, representing the target
606
606
  temperature in °C.
607
607
 
608
- .. note:
608
+ .. note::
609
609
 
610
610
  The Thermocycler will proceed to the next command immediately after
611
611
  ``temperature`` has been reached.
@@ -635,13 +635,13 @@ class ThermocyclerContext(ModuleContext):
635
635
  individual well of the loaded labware, in µL.
636
636
  If not specified, the default is 25 µL.
637
637
 
638
- .. note:
638
+ .. note::
639
639
 
640
640
  Unlike with :py:meth:`set_block_temperature`, either or both of
641
641
  ``hold_time_minutes`` and ``hold_time_seconds`` must be defined
642
642
  and for each step.
643
643
 
644
- .. note:
644
+ .. note::
645
645
 
646
646
  Before API Version 2.21, Thermocycler profiles run with this command
647
647
  would be listed in the app as having a number of repetitions equal to
@@ -991,7 +991,7 @@ class MagneticBlockContext(ModuleContext):
991
991
 
992
992
 
993
993
  class AbsorbanceReaderContext(ModuleContext):
994
- """An object representing a connected Absorbance Reader Module.
994
+ """An object representing a connected Absorbance Plate Reader Module.
995
995
 
996
996
  It should not be instantiated directly; instead, it should be
997
997
  created through :py:meth:`.ProtocolContext.load_module`.
@@ -1009,17 +1009,21 @@ class AbsorbanceReaderContext(ModuleContext):
1009
1009
 
1010
1010
  @requires_version(2, 21)
1011
1011
  def close_lid(self) -> None:
1012
- """Close the lid of the Absorbance Reader."""
1012
+ """Use the Flex Gripper to close the lid of the Absorbance Plate Reader.
1013
+
1014
+ You must call this method before initializing the reader, even if the reader was
1015
+ in the closed position at the start of the protocol.
1016
+ """
1013
1017
  self._core.close_lid()
1014
1018
 
1015
1019
  @requires_version(2, 21)
1016
1020
  def open_lid(self) -> None:
1017
- """Open the lid of the Absorbance Reader."""
1021
+ """Use the Flex Gripper to open the lid of the Absorbance Plate Reader."""
1018
1022
  self._core.open_lid()
1019
1023
 
1020
1024
  @requires_version(2, 21)
1021
1025
  def is_lid_on(self) -> bool:
1022
- """Return ``True`` if the Absorbance Reader's lid is currently closed."""
1026
+ """Return ``True`` if the Absorbance Plate Reader's lid is currently closed."""
1023
1027
  return self._core.is_lid_on()
1024
1028
 
1025
1029
  @requires_version(2, 21)
@@ -1029,19 +1033,28 @@ class AbsorbanceReaderContext(ModuleContext):
1029
1033
  wavelengths: List[int],
1030
1034
  reference_wavelength: Optional[int] = None,
1031
1035
  ) -> None:
1032
- """Take a zero reading on the Absorbance Plate Reader Module.
1036
+ """Prepare the Absorbance Plate Reader to read a plate.
1037
+
1038
+ See :ref:`absorbance-initialization` for examples.
1033
1039
 
1034
1040
  :param mode: Either ``"single"`` or ``"multi"``.
1035
1041
 
1036
- - In single measurement mode, :py:meth:`.AbsorbanceReaderContext.read` uses
1037
- one sample wavelength and an optional reference wavelength.
1038
- - In multiple measurement mode, :py:meth:`.AbsorbanceReaderContext.read` uses
1039
- a list of up to six sample wavelengths.
1040
- :param wavelengths: A list of wavelengths, in mm, to measure.
1041
- - Must contain only one item when initializing a single measurement.
1042
- - Must contain one to six items when initializing a multiple measurement.
1043
- :param reference_wavelength: An optional reference wavelength, in mm. Cannot be
1044
- used with multiple measurements.
1042
+ - In single measurement mode, :py:meth:`.AbsorbanceReaderContext.read` uses
1043
+ one sample wavelength and an optional reference wavelength.
1044
+ - In multiple measurement mode, :py:meth:`.AbsorbanceReaderContext.read` uses
1045
+ a list of up to six sample wavelengths.
1046
+ :param wavelengths: A list of wavelengths, in nm, to measure.
1047
+
1048
+ - In the default hardware configuration, each wavelength must be one of
1049
+ ``450`` (blue), ``562`` (green), ``600`` (orange), or ``650`` (red). In
1050
+ custom hardware configurations, the module may accept other integers
1051
+ between 350 and 1000.
1052
+ - The list must contain only one item when initializing a single measurement.
1053
+ - The list can contain one to six items when initializing a multiple measurement.
1054
+ :param reference_wavelength: An optional reference wavelength, in nm. If provided,
1055
+ :py:meth:`.AbsorbanceReaderContext.read` will read at the reference
1056
+ wavelength and then subtract the reference wavelength values from the
1057
+ measurement wavelength values. Can only be used with single measurements.
1045
1058
  """
1046
1059
  self._core.initialize(
1047
1060
  mode, wavelengths, reference_wavelength=reference_wavelength
@@ -1051,16 +1064,33 @@ class AbsorbanceReaderContext(ModuleContext):
1051
1064
  def read(
1052
1065
  self, export_filename: Optional[str] = None
1053
1066
  ) -> Dict[int, Dict[str, float]]:
1054
- """Initiate read on the Absorbance Reader.
1067
+ """Read a plate on the Absorbance Plate Reader.
1068
+
1069
+ This method always returns a dictionary of measurement data. It optionally will
1070
+ save a CSV file of the results to the Flex filesystem, which you can access from
1071
+ the Recent Protocol Runs screen in the Opentrons App. These files are `only` saved
1072
+ if you specify ``export_filename``.
1073
+
1074
+ In simulation, the values for each well key in the dictionary are set to zero, and
1075
+ no files are written.
1076
+
1077
+ .. note::
1078
+
1079
+ Avoid divide-by-zero errors when simulating and using the results of this
1080
+ method later in the protocol. If you divide by any of the measurement
1081
+ values, use :py:meth:`.ProtocolContext.is_simulating` to use alternate dummy
1082
+ data or skip the division step.
1055
1083
 
1056
- Returns a dictionary of wavelengths to dictionary of values ordered by well name.
1084
+ :param export_filename: An optional file basename. If provided, this method
1085
+ will write a CSV file for each measurement in the read operation. File
1086
+ names will use the value of this parameter, the measurement wavelength
1087
+ supplied in :py:meth:`~.AbsorbanceReaderContext.initialize`, and a
1088
+ ``.csv`` extension. For example, when reading at wavelengths 450 and 562
1089
+ with ``export_filename="my_data"``, there will be two output files:
1090
+ ``my_data_450.csv`` and ``my_data_562.csv``.
1057
1091
 
1058
- :param export_filename: Optional, if a filename is provided a CSV file will be saved
1059
- as a result of the read action containing measurement data. The filename will
1060
- be modified to include the wavelength used during measurement. If multiple
1061
- measurements are taken, then a file will be generated for each wavelength provided.
1092
+ See :ref:`absorbance-csv` for information on working with these CSV files.
1062
1093
 
1063
- Example: If `export_filename="my_data"` and wavelengths 450 and 531 are used during
1064
- measurement, the output files will be "my_data_450.csv" and "my_data_531.csv".
1094
+ :returns: A dictionary of wavelengths to dictionary of values ordered by well name.
1065
1095
  """
1066
1096
  return self._core.read(filename=export_filename)
@@ -10,6 +10,7 @@ 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
13
14
 
14
15
  if TYPE_CHECKING:
15
16
  from opentrons.protocol_engine.state.state import StateView
@@ -69,30 +70,41 @@ class InitializeImpl(
69
70
  unsupported_wavelengths = sample_wavelengths.difference(
70
71
  supported_wavelengths
71
72
  )
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
+ )
72
78
  if unsupported_wavelengths:
73
- raise ValueError(f"Unsupported wavelengths: {unsupported_wavelengths}")
79
+ raise InvalidWavelengthError(
80
+ f"Unsupported wavelengths: {unsupported_wl_str}. "
81
+ f" Use one of {supported_wl_str} instead."
82
+ )
74
83
 
75
84
  if params.measureMode == "single":
76
85
  if sample_wavelengths_len != 1:
77
86
  raise ValueError(
78
- f"single requires one sample wavelength, provided {sample_wavelengths}"
87
+ f"Measure mode `single` requires one sample wavelength,"
88
+ f" {sample_wl_str} provided instead."
79
89
  )
80
90
  if (
81
91
  reference_wavelength is not None
82
92
  and reference_wavelength not in supported_wavelengths
83
93
  ):
84
- raise ValueError(
85
- f"Reference wavelength {reference_wavelength} not supported {supported_wavelengths}"
94
+ raise InvalidWavelengthError(
95
+ f"Reference wavelength {reference_wavelength}nm is not supported."
96
+ f" Use one of {supported_wl_str} instead."
86
97
  )
87
98
 
88
99
  if params.measureMode == "multi":
89
100
  if sample_wavelengths_len < 1 or sample_wavelengths_len > 6:
90
101
  raise ValueError(
91
- f"multi requires 1-6 sample wavelengths, provided {sample_wavelengths}"
102
+ f"Measure mode `multi` requires 1-6 sample wavelengths,"
103
+ f" {sample_wl_str} provided instead."
92
104
  )
93
105
  if reference_wavelength is not None:
94
- raise RuntimeError(
95
- "Reference wavelength cannot be used with multi mode."
106
+ raise ValueError(
107
+ "Reference wavelength cannot be used with Measure mode `multi`."
96
108
  )
97
109
 
98
110
  await abs_reader.set_sample_wavelength(
@@ -83,10 +83,11 @@ 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
+
86
88
  ready_to_aspirate = self._pipetting.get_is_ready_to_aspirate(
87
89
  pipette_id=params.pipetteId,
88
90
  )
89
-
90
91
  if not ready_to_aspirate:
91
92
  raise PipetteNotReadyToAspirateError(
92
93
  "Pipette cannot aspirate in place because of a previous blow out."
@@ -94,11 +95,10 @@ class AspirateInPlaceImplementation(
94
95
  " so the plunger can be reset in a known safe position."
95
96
  )
96
97
 
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)
99
100
 
100
101
  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,7 +185,9 @@ class BaseCommand(
185
185
  )
186
186
  error: Union[
187
187
  _ErrorT,
188
- # ErrorOccurrence here is for undefined errors not captured by _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.
189
191
  ErrorOccurrence,
190
192
  None,
191
193
  ] = 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)
79
80
  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,6 +145,7 @@ class DropTipImplementation(AbstractCommandImpl[DropTipParams, _ExecuteReturn]):
145
145
  error=exception,
146
146
  )
147
147
  ],
148
+ errorInfo={"retryLocation": position},
148
149
  )
149
150
  state_update_if_false_positive = update_types.StateUpdate()
150
151
  state_update_if_false_positive.update_pipette_tip_state(
@@ -165,7 +166,7 @@ class DropTipImplementation(AbstractCommandImpl[DropTipParams, _ExecuteReturn]):
165
166
  )
166
167
 
167
168
 
168
- class DropTip(BaseCommand[DropTipParams, DropTipResult, ErrorOccurrence]):
169
+ class DropTip(BaseCommand[DropTipParams, DropTipResult, TipPhysicallyAttachedError]):
169
170
  """Drop tip command model."""
170
171
 
171
172
  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
21
+ from ..execution import TipHandler, GantryMover
22
22
 
23
23
 
24
24
  DropTipInPlaceCommandType = Literal["dropTipInPlace"]
@@ -57,15 +57,19 @@ class DropTipInPlaceImplementation(
57
57
  self,
58
58
  tip_handler: TipHandler,
59
59
  model_utils: ModelUtils,
60
+ gantry_mover: GantryMover,
60
61
  **kwargs: object,
61
62
  ) -> None:
62
63
  self._tip_handler = tip_handler
63
64
  self._model_utils = model_utils
65
+ self._gantry_mover = gantry_mover
64
66
 
65
67
  async def execute(self, params: DropTipInPlaceParams) -> _ExecuteReturn:
66
68
  """Drop a tip using the requested pipette."""
67
69
  state_update = update_types.StateUpdate()
68
70
 
71
+ retry_location = await self._gantry_mover.get_position(params.pipetteId)
72
+
69
73
  try:
70
74
  await self._tip_handler.drop_tip(
71
75
  pipette_id=params.pipetteId, home_after=params.homeAfter
@@ -85,6 +89,7 @@ class DropTipInPlaceImplementation(
85
89
  error=exception,
86
90
  )
87
91
  ],
92
+ errorInfo={"retryLocation": retry_location},
88
93
  )
89
94
  return DefinedErrorData(
90
95
  public=error,
@@ -99,7 +104,7 @@ class DropTipInPlaceImplementation(
99
104
 
100
105
 
101
106
  class DropTipInPlace(
102
- BaseCommand[DropTipInPlaceParams, DropTipInPlaceResult, ErrorOccurrence]
107
+ BaseCommand[DropTipInPlaceParams, DropTipInPlaceResult, TipPhysicallyAttachedError]
103
108
  ):
104
109
  """Drop tip in place command model."""
105
110
 
@@ -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,7 +148,12 @@ class DestinationPositionResult(BaseModel):
148
148
 
149
149
 
150
150
  class ErrorLocationInfo(TypedDict):
151
- """Holds a retry location for in-place error recovery."""
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
+ """
152
157
 
153
158
  retryLocation: Tuple[float, float, float]
154
159
 
@@ -201,3 +206,5 @@ class TipPhysicallyAttachedError(ErrorOccurrence):
201
206
 
202
207
  errorCode: str = ErrorCodes.TIP_DROP_FAILED.value.code
203
208
  detail: str = ErrorCodes.TIP_DROP_FAILED.value.detail
209
+
210
+ errorInfo: ErrorLocationInfo
@@ -129,9 +129,14 @@ class UnsafePlaceLabwareImplementation(
129
129
  module.id
130
130
  )
131
131
 
132
- # NOTE: When the estop is pressed, the gantry loses position,
133
- # so the robot needs to home x, y to sync.
134
- await ot3api.home(axes=[Axis.Z_L, Axis.Z_R, Axis.Z_G, Axis.X, Axis.Y])
132
+ # NOTE: When the estop is pressed, the gantry loses position, lets use
133
+ # the encoders to sync position.
134
+ # Ideally, we'd do a full home, but this command is used when
135
+ # the gripper is holding the plate reader, and a full home would
136
+ # bang it into the right window.
137
+ await ot3api.home(axes=[Axis.Z_L, Axis.Z_R, Axis.Z_G])
138
+ await ot3api.engage_axes([Axis.X, Axis.Y])
139
+ await ot3api.update_axis_position_estimations([Axis.X, Axis.Y])
135
140
 
136
141
  # Place the labware down
137
142
  await self._start_movement(ot3api, definition, location, drop_offset)
@@ -23,7 +23,11 @@ class UpdatePositionEstimatorsParams(BaseModel):
23
23
  """Payload required for an UpdatePositionEstimators command."""
24
24
 
25
25
  axes: List[MotorAxis] = Field(
26
- ..., description="The axes for which to update the position estimators."
26
+ ...,
27
+ description=(
28
+ "The axes for which to update the position estimators."
29
+ " Any axes that are not physically present will be ignored."
30
+ ),
27
31
  )
28
32
 
29
33
 
@@ -55,6 +55,7 @@ from .exceptions import (
55
55
  InvalidTargetTemperatureError,
56
56
  InvalidBlockVolumeError,
57
57
  InvalidHoldTimeError,
58
+ InvalidWavelengthError,
58
59
  CannotPerformModuleAction,
59
60
  PauseNotAllowedError,
60
61
  ResumeFromRecoveryNotAllowedError,
@@ -137,6 +138,7 @@ __all__ = [
137
138
  "InvalidTargetSpeedError",
138
139
  "InvalidBlockVolumeError",
139
140
  "InvalidHoldTimeError",
141
+ "InvalidWavelengthError",
140
142
  "CannotPerformModuleAction",
141
143
  "ResumeFromRecoveryNotAllowedError",
142
144
  "PauseNotAllowedError",
@@ -12,8 +12,6 @@ from opentrons_shared_data.errors.exceptions import EnumeratedError
12
12
  log = getLogger(__name__)
13
13
 
14
14
 
15
- # TODO(mc, 2021-11-12): flesh this model out with structured error data
16
- # for each error type so client may produce better error messages
17
15
  class ErrorOccurrence(BaseModel):
18
16
  """An occurrence of a specific error during protocol execution."""
19
17
 
@@ -44,8 +42,15 @@ class ErrorOccurrence(BaseModel):
44
42
  id: str = Field(..., description="Unique identifier of this error occurrence.")
45
43
  createdAt: datetime = Field(..., description="When the error occurred.")
46
44
 
45
+ # Our Python should probably always set this to False--if we want it to be True,
46
+ # we should probably be using a more specific subclass of ErrorOccurrence anyway.
47
+ # However, we can't make this Literal[False], because we want this class to be able
48
+ # to act as a catch-all for parsing defined errors that might be missing some
49
+ # `errorInfo` fields because they were serialized by older software.
47
50
  isDefined: bool = Field(
48
- default=False, # default=False for database backwards compatibility.
51
+ # default=False for database backwards compatibility, so we can parse objects
52
+ # serialized before isDefined existed.
53
+ default=False,
49
54
  description=dedent(
50
55
  """\
51
56
  Whether this error is *defined.*
@@ -773,6 +773,19 @@ class InvalidBlockVolumeError(ProtocolEngineError):
773
773
  super().__init__(ErrorCodes.GENERAL_ERROR, message, details, wrapping)
774
774
 
775
775
 
776
+ class InvalidWavelengthError(ProtocolEngineError):
777
+ """Raised when attempting to set an invalid absorbance wavelength."""
778
+
779
+ def __init__(
780
+ self,
781
+ message: Optional[str] = None,
782
+ details: Optional[Dict[str, Any]] = None,
783
+ wrapping: Optional[Sequence[EnumeratedError]] = None,
784
+ ) -> None:
785
+ """Build a InvalidWavelengthError."""
786
+ super().__init__(ErrorCodes.GENERAL_ERROR, message, details, wrapping)
787
+
788
+
776
789
  class InvalidHoldTimeError(ProtocolEngineError):
777
790
  """An error raised when attempting to set an invalid temperature hold time."""
778
791
 
@@ -418,6 +418,21 @@ class RunOrchestrator:
418
418
  """Get current nozzle maps keyed by pipette id."""
419
419
  return self._protocol_engine.state_view.tips.get_pipette_nozzle_maps()
420
420
 
421
+ def get_tip_attached(self) -> Dict[str, bool]:
422
+ """Get current tip state keyed by pipette id."""
423
+
424
+ def has_tip_attached(pipette_id: str) -> bool:
425
+ return (
426
+ self._protocol_engine.state_view.pipettes.get_attached_tip(pipette_id)
427
+ is not None
428
+ )
429
+
430
+ pipette_ids = (
431
+ pipette.id
432
+ for pipette in self._protocol_engine.state_view.pipettes.get_all()
433
+ )
434
+ return {pipette_id: has_tip_attached(pipette_id) for pipette_id in pipette_ids}
435
+
421
436
  def set_error_recovery_policy(self, policy: ErrorRecoveryPolicy) -> None:
422
437
  """Create error recovery policy for the run."""
423
438
  self._protocol_engine.set_error_recovery_policy(policy)
@@ -60,7 +60,9 @@ class CSVParameter:
60
60
  as appropriate.
61
61
 
62
62
  :param detect_dialect: If ``True``, examine the file and try to assign it a
63
- :py:class:`csv.Dialect` to improve parsing behavior.
63
+ :py:class:`csv.Dialect` to improve parsing behavior. Set this to ``False``
64
+ when using the file output of :py:meth:`.AbsorbanceReaderContext.read` as
65
+ a runtime parameter.
64
66
  :param kwargs: For advanced CSV handling, you can pass any of the
65
67
  `formatting parameters <https://docs.python.org/3/library/csv.html#csv-fmt-params>`_
66
68
  accepted by :py:func:`csv.reader` from the Python standard library.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: opentrons
3
- Version: 8.2.0a1
3
+ Version: 8.2.0a3
4
4
  Summary: The Opentrons API is a simple framework designed to make writing automated biology lab protocols easy.
5
5
  Author: Opentrons
6
6
  Author-email: engineering@opentrons.com
@@ -21,7 +21,7 @@ Classifier: Programming Language :: Python :: 3.10
21
21
  Classifier: Topic :: Scientific/Engineering
22
22
  Requires-Python: >=3.10
23
23
  License-File: ../LICENSE
24
- Requires-Dist: opentrons-shared-data ==8.2.0a1
24
+ Requires-Dist: opentrons-shared-data ==8.2.0a3
25
25
  Requires-Dist: aionotify ==0.3.1
26
26
  Requires-Dist: anyio <4.0.0,>=3.6.1
27
27
  Requires-Dist: jsonschema <4.18.0,>=3.0.1
@@ -34,9 +34,9 @@ Requires-Dist: pyusb ==1.2.1
34
34
  Requires-Dist: packaging >=21.0
35
35
  Requires-Dist: importlib-metadata >=1.0 ; python_version < "3.8"
36
36
  Provides-Extra: flex-hardware
37
- Requires-Dist: opentrons-hardware[flex] ==8.2.0a1 ; extra == 'flex-hardware'
37
+ Requires-Dist: opentrons-hardware[flex] ==8.2.0a3 ; extra == 'flex-hardware'
38
38
  Provides-Extra: ot2-hardware
39
- Requires-Dist: opentrons-hardware ==8.2.0a1 ; extra == 'ot2-hardware'
39
+ Requires-Dist: opentrons-hardware ==8.2.0a3 ; extra == 'ot2-hardware'
40
40
 
41
41
  .. _Full API Documentation: http://docs.opentrons.com
42
42
 
@@ -47,7 +47,7 @@ opentrons/drivers/types.py,sha256=muQKlqOMYcL6iglT9a3iEJ5EnqcBDCkgHz2KyePsm6I,22
47
47
  opentrons/drivers/utils.py,sha256=QmSTP07PPXq3_Qth1Idagn7lixWFjpkOzR8LFSpcw-g,7362
48
48
  opentrons/drivers/absorbance_reader/__init__.py,sha256=d9_-VJ_MKOyRy9C5Ioeg4CWsC1WIVgQlxGqBvgpoBRc,322
49
49
  opentrons/drivers/absorbance_reader/abstract.py,sha256=Egc7RKKXscEXFC0bPVx_r1vKe2-Df9PJyDpH0OFOIiw,2005
50
- opentrons/drivers/absorbance_reader/async_byonoy.py,sha256=pp4lvODBd3ydKwac28RANQgWs_X5Ml2NdFw0ZHgpXPA,13502
50
+ opentrons/drivers/absorbance_reader/async_byonoy.py,sha256=zmRC1nTHDx1X_paN7mSPYdHMtKtKpmrs0KnEUDHdD58,13558
51
51
  opentrons/drivers/absorbance_reader/driver.py,sha256=V8hLjNRVzlRPtVW04XEWrXytn06ZRBxG9LqL4ETj21g,2791
52
52
  opentrons/drivers/absorbance_reader/hid_protocol.py,sha256=OM6Ogkl1Lw3d501rfHcRI3lPZITAVKdxCR6JkHdKMCQ,3836
53
53
  opentrons/drivers/absorbance_reader/simulator.py,sha256=MiFQgnVNq2R35T3u59idcKlrj6SEFllt8tdupfH5jKQ,2419
@@ -100,14 +100,14 @@ opentrons/hardware_control/module_control.py,sha256=u0RpbOgr5pV5exgKg3qvNQdwdVuy
100
100
  opentrons/hardware_control/motion_utilities.py,sha256=LuOZBcnNJmTPra6-mYX5wN3jh8PA2l81dy5amCyYpcQ,7164
101
101
  opentrons/hardware_control/nozzle_manager.py,sha256=XNEyNdh00QdZ7VECb--JUO_BI-96EwoAPuQ7bdGXSGs,16788
102
102
  opentrons/hardware_control/ot3_calibration.py,sha256=ExWZDLDHpi8HUzdYvBzhOyAXXwT3z7BnxPH-2EpOerM,44717
103
- opentrons/hardware_control/ot3api.py,sha256=QCTBbJ2-KLFk43qCkEJctxSmk9ZwMV8Tj6QN89KFjXo,118083
103
+ opentrons/hardware_control/ot3api.py,sha256=CMtyrIDYzw-H-FQuUlnjVpGEOA9YNWETFy7jDxT8zYk,118073
104
104
  opentrons/hardware_control/pause_manager.py,sha256=wmNmraimE2yZQVqCxX_rtQHUWRzpzyQEaym9fLMgyww,888
105
105
  opentrons/hardware_control/poller.py,sha256=rx6Q-UxbUIj3ek69IlYenFh7BFuasszRO-GzzhRPuHQ,3885
106
106
  opentrons/hardware_control/robot_calibration.py,sha256=HiCQNmdp59SbkzXpDGtPsN8rSfUj-ZU4v63vcSw4AbI,7149
107
107
  opentrons/hardware_control/simulator_setup.py,sha256=3gelKslgMDobLpIlhVHmAqmZ5wXe1hZcK4RwKRMy3aM,9277
108
108
  opentrons/hardware_control/thread_manager.py,sha256=3-PrzUzJFSjUVtrgTUEF5GIStsDufvaSbp51B2x-yJY,17253
109
109
  opentrons/hardware_control/threaded_async_lock.py,sha256=qMaIrJ4KSMzvKm0hWt4o74Joah-sc404LlhMjCJue2g,3316
110
- opentrons/hardware_control/types.py,sha256=1A0-2eWLXRWsbTJKSjm_OGyqLZiX5z9aaKN_AvOJE5o,20691
110
+ opentrons/hardware_control/types.py,sha256=Df4FGgsoPeTiX5EQes4MGW4O1hoJxLI0SU3Fo-whsPs,20777
111
111
  opentrons/hardware_control/util.py,sha256=RSlfjzrmhtntSexamNpPn3-qrnbrdhuzEjOnxv8v7lA,8650
112
112
  opentrons/hardware_control/backends/__init__.py,sha256=u5Dg3AFZuvDV7hFqJ8I4F9D1dLDt6e-TsFJReyBYe4E,212
113
113
  opentrons/hardware_control/backends/controller.py,sha256=MOVMq9s1rY_jHhajHB1hQ1MgXXyY54-gMrrvAuMsOFg,14622
@@ -116,7 +116,7 @@ opentrons/hardware_control/backends/estop_state.py,sha256=_GYjI6OaD3CZNduWV2_RVe
116
116
  opentrons/hardware_control/backends/flex_protocol.py,sha256=PlGUMXTRISFl9c-ZxU2TdPDursLAprPw6ZLqtGMngIE,12030
117
117
  opentrons/hardware_control/backends/ot3controller.py,sha256=3y18NJbFDKMkfLk4X8ApIcZ9n3EMtZqCqag5hTUS74g,61020
118
118
  opentrons/hardware_control/backends/ot3simulator.py,sha256=N63d7Qf1DHg_-sVxsRZsKoJIZtTRJIRVNZL2V_EE5Os,29502
119
- opentrons/hardware_control/backends/ot3utils.py,sha256=3y3jb0NdveIXd06X7wllfkx2hkc5NYtxwRrr8OxW1Vs,21893
119
+ opentrons/hardware_control/backends/ot3utils.py,sha256=Y0V26XqPxKzZmMhc0pBdfibC_En9pWmU77kyD7tDHOg,21955
120
120
  opentrons/hardware_control/backends/simulator.py,sha256=q_9PQlBdOyCa9sj2gLqYWZ-fG9v4mddDAiScL-yHCXY,17549
121
121
  opentrons/hardware_control/backends/status_bar_state.py,sha256=ftNn2ouBhPZiFbUm79I4z6ndup7XDmcNowhb-KREspE,8529
122
122
  opentrons/hardware_control/backends/subsystem_manager.py,sha256=lv2ON2pZ7PY2tg3-qxpAlLYIg1SJxX3erHJvcfy5UGs,16484
@@ -160,7 +160,7 @@ opentrons/hardware_control/instruments/ot3/instrument_calibration.py,sha256=9ERA
160
160
  opentrons/hardware_control/instruments/ot3/pipette.py,sha256=rrbN1Du1pTL8-pZ19K38ns_w5SMDMHcUG3Wh5nuxdWo,32493
161
161
  opentrons/hardware_control/instruments/ot3/pipette_handler.py,sha256=G6cL5qjwgi4hXKUNwvnJDEaeX0u2xp-U-dEHLRo_5pc,36704
162
162
  opentrons/hardware_control/modules/__init__.py,sha256=YLYJShv7FDI_QHboB2lQkSxoILoW9ms6Opu45ZeMMBU,1306
163
- opentrons/hardware_control/modules/absorbance_reader.py,sha256=dCbOyHvQdN3KXh2EUnLuD1C6Wpe-ft-dmcfX4H39KY8,13468
163
+ opentrons/hardware_control/modules/absorbance_reader.py,sha256=Z6ikQiySpDi4tP1VxT-Q0WCNLUqfyzJ-iw7B7t-CUNI,13573
164
164
  opentrons/hardware_control/modules/errors.py,sha256=cREqoMc6nwGxQbLvZYDfIlq1mCv0alN42J7qxNBNiOY,165
165
165
  opentrons/hardware_control/modules/heater_shaker.py,sha256=tp-j8jbkY1oRrvHkXwmMRng-tX4ARPPrEJsS1mSQFCo,15026
166
166
  opentrons/hardware_control/modules/lid_temp_status.py,sha256=XKlr41I41g8aFxrrqGZxecHWgtBceG9ZrOSkyqq1khE,1201
@@ -189,7 +189,7 @@ opentrons/hardware_control/protocols/instrument_configurer.py,sha256=5zUCAchtoJ6
189
189
  opentrons/hardware_control/protocols/liquid_handler.py,sha256=UusmQYUncMqAwxuKTIcIIrlF93XpLPctu5SJuotsTws,7731
190
190
  opentrons/hardware_control/protocols/module_provider.py,sha256=QDKCWqrW-6IeI91IICBTJClK0C__mgq3A0-M3Wa9ee8,487
191
191
  opentrons/hardware_control/protocols/motion_controller.py,sha256=2sv-fc0uvmuFj-wA1h4VrEjLLTMTvswLCkisjEXwxXQ,9520
192
- opentrons/hardware_control/protocols/position_estimator.py,sha256=bEYQiNZYsh5k9r_J2XCDQg6FixrQmxr-7KTD9gfVmYc,1797
192
+ opentrons/hardware_control/protocols/position_estimator.py,sha256=BrqK5AJn9747c4LX0ZWBJWgWHjyX977CHBI7WVvO-9Q,1922
193
193
  opentrons/hardware_control/protocols/simulatable.py,sha256=ED3VHoO8q1h9FhBDv31g5N7YdTKB5hj7lp7BZcCaL7o,247
194
194
  opentrons/hardware_control/protocols/stoppable.py,sha256=ukI1WrJzXwsJm5ty2trhMqGJr0sT13ttlv914YMAUt8,226
195
195
  opentrons/hardware_control/protocols/types.py,sha256=UlejXW-ZHjuZWizKamphyGG4Iv7-liOuCfvQR29f0Do,613
@@ -222,7 +222,7 @@ opentrons/protocol_api/deck.py,sha256=94vFceg1SC1bAGd7TvC1ZpYwnJR-VlzurEZ6jkacYe
222
222
  opentrons/protocol_api/disposal_locations.py,sha256=NRiSGmDR0LnbyEkWSOM-o64uR2fUoB1NWJG7Y7SsJSs,7920
223
223
  opentrons/protocol_api/instrument_context.py,sha256=EnOs4BAmpIabaWMFKCqhwIJcrWpP-iEv32kmdV2t9Uo,97275
224
224
  opentrons/protocol_api/labware.py,sha256=qwjAO1Au9Ujx5jyGNQ0-HLDBOXUygnXNSuv5696tGCw,48177
225
- opentrons/protocol_api/module_contexts.py,sha256=TZiswcxFRSFP_t_OoE03BSnOkZo9XQjoayHiUlVeqwA,40455
225
+ opentrons/protocol_api/module_contexts.py,sha256=qubmR4LrLwP8e-ONKQYBlLTRQ5gpB-H25fE3SV70sKI,42104
226
226
  opentrons/protocol_api/module_validation_and_errors.py,sha256=XL_m72P8rcvGO2fynY7UzXLcpGuI6X4s0V6Xf735Iyc,1464
227
227
  opentrons/protocol_api/protocol_context.py,sha256=3nVIYxTpFp2Ho6kdW65hG8LhDaTRvlFuGbuy9LsQg5s,55459
228
228
  opentrons/protocol_api/robot_context.py,sha256=vph_ZqfdmREOwLwpjSkXiSZSpI1HO0HuilhqjhgT7Rw,2660
@@ -281,19 +281,19 @@ opentrons/protocol_engine/clients/sync_client.py,sha256=f65mhTO3zwTph1LtCl7Td6z3
281
281
  opentrons/protocol_engine/clients/transports.py,sha256=QejmtG5TjgOLUXpNhOzor_q-7MFI1qNfsguwkzY6Rxc,7177
282
282
  opentrons/protocol_engine/commands/__init__.py,sha256=FKOX8Uq3g2_syxMQN0A70qEtpJrsJDlJfPhAxS4ZAII,14006
283
283
  opentrons/protocol_engine/commands/aspirate.py,sha256=zOh7wqqFlAdkQCUC1-pp0-Zi-6QEpg3ntXDTpzlgc4Y,6137
284
- opentrons/protocol_engine/commands/aspirate_in_place.py,sha256=0m3V2IMZm4PgekIPHkF9f7v52j3ngDNJuoppmF0p4xU,6003
284
+ opentrons/protocol_engine/commands/aspirate_in_place.py,sha256=pWU3Rfgi3i1vHG8_cKFmkot1sYKAYSxR-iQJlMpjTo4,5999
285
285
  opentrons/protocol_engine/commands/blow_out.py,sha256=qCxq_wa5spsr7kARCR1lRIODF4WQDCsZFJ4rhtJ4FXw,4191
286
286
  opentrons/protocol_engine/commands/blow_out_in_place.py,sha256=uJ6x8cXM1WcIKk6771FlVRLnO9-p5C8_FBCq0aGralk,3809
287
- opentrons/protocol_engine/commands/command.py,sha256=xBJBC2N40Ljkyp7hh5riXAu_2BWrgajU95uRVNvA3GI,9780
287
+ opentrons/protocol_engine/commands/command.py,sha256=LmZQAGRw_QnB77QNo242nonefqk1chg9A-MQ75F6GPs,9952
288
288
  opentrons/protocol_engine/commands/command_unions.py,sha256=30mrXVFxikW24rs3MeVBhpCevEbxs8NZ7EygLfi7O1M,20402
289
289
  opentrons/protocol_engine/commands/comment.py,sha256=yo2MpBEkILZ0R501eBW0-UACi15daH4COmghdDPz_FQ,1631
290
290
  opentrons/protocol_engine/commands/configure_for_volume.py,sha256=ZAZMWpFDU8C4GNRO-AEuMk1J2um11U5-iY3s3AaNi_M,3293
291
291
  opentrons/protocol_engine/commands/configure_nozzle_layout.py,sha256=86cYz78pVPtIiZu71TegHBIH9Gzz3Rz3LxI5-9bUJ5w,3802
292
292
  opentrons/protocol_engine/commands/custom.py,sha256=Gdhg8QrQ-PKphW1j8j3O2JEeCTQSQldldtNE-3YIdLU,2187
293
293
  opentrons/protocol_engine/commands/dispense.py,sha256=LcOjG9kzf0yUcnCBLGV6OmTur1Y_6B6ydUUvo3LFFok,5033
294
- opentrons/protocol_engine/commands/dispense_in_place.py,sha256=uJP947lhql7OrJtfcZUhiSN7nKnJsTGtDVkGp4aCpSI,5187
295
- opentrons/protocol_engine/commands/drop_tip.py,sha256=DdRPYbafoahQLkrgBG7X9GvzDxuyuz7oLbUeLcBC-Ko,6272
296
- opentrons/protocol_engine/commands/drop_tip_in_place.py,sha256=_FUBZ-FcmOoK57zZLrjCl_FSRk0RzUY_M1mGL2Js9vU,3914
294
+ opentrons/protocol_engine/commands/dispense_in_place.py,sha256=oiek-Xu3TvGv2tQY6BHcp4r1LW4bu1mAB58Nyr3FKWI,5183
295
+ opentrons/protocol_engine/commands/drop_tip.py,sha256=rjR7lAf20OvaFSRVgl78fFq_9dUW6U7-N7eHsq3hoK0,6338
296
+ opentrons/protocol_engine/commands/drop_tip_in_place.py,sha256=qTPwqXWkMW4rIQznxi3anRjCBH2UQE_WlcIHCPXypvc,4158
297
297
  opentrons/protocol_engine/commands/generate_command_schema.py,sha256=w5RsTJV4HtFpS58-xfVQ2O52h1R0zVTJkpLapN4uo9Y,1352
298
298
  opentrons/protocol_engine/commands/get_tip_presence.py,sha256=igvMiKJWzdetWBXlIaYp4Tfk57gfACon34CWzUWynno,2536
299
299
  opentrons/protocol_engine/commands/hash_command_params.py,sha256=obWy4TbVH97SyhNqrSD6iP1wgZ20JoaH1rilZCjXxIs,1530
@@ -309,8 +309,8 @@ opentrons/protocol_engine/commands/move_to_addressable_area.py,sha256=cJRXXRLkaw
309
309
  opentrons/protocol_engine/commands/move_to_addressable_area_for_drop_tip.py,sha256=5BAisk0npPcesI6bMQxKH2Xxz641uyR6RbtU9RiD5NM,7134
310
310
  opentrons/protocol_engine/commands/move_to_coordinates.py,sha256=FQ1wQ9V3OcM4nSn-hWYWrxefcLMLZOw8WW6yARiRqDg,3051
311
311
  opentrons/protocol_engine/commands/move_to_well.py,sha256=0w5TsY51m5rmp-H7bd9EKWjTzJiz3ztyW647N5NxdHI,3292
312
- opentrons/protocol_engine/commands/pick_up_tip.py,sha256=Qd7ZZEKNT1cwlNZUZgZqtjnwJ7Iqgpf4dhIcbuf_0W0,6875
313
- opentrons/protocol_engine/commands/pipetting_common.py,sha256=Ds5FsMpBIxq2Y-2_ntGTsjC0eFZReXDSmVdk37LJOSI,6672
312
+ opentrons/protocol_engine/commands/pick_up_tip.py,sha256=1yzOenpKUDIQh_xrkea5hhD_0kxd00DmXpoIrvr6kuo,6874
313
+ opentrons/protocol_engine/commands/pipetting_common.py,sha256=JW_QzmBhwOXq_xONiSNyeHdLzFn2r--H6pDu05JNSJE,6879
314
314
  opentrons/protocol_engine/commands/prepare_to_aspirate.py,sha256=061KttjjIM4u5WejUViLwnIqrj97mr8V-4UjkbTbitU,3710
315
315
  opentrons/protocol_engine/commands/reload_labware.py,sha256=XPSQ1AHykiChI1CotwE73eF7FbULUqDQ1pUpOtEwrXc,3408
316
316
  opentrons/protocol_engine/commands/retract_axis.py,sha256=bMPJxQSYK9VsxraNl_SErXH9Dj4pBqrbx2gtTIg0MMw,2882
@@ -323,7 +323,7 @@ opentrons/protocol_engine/commands/wait_for_duration.py,sha256=DIATqpdBStGQWnLAb
323
323
  opentrons/protocol_engine/commands/wait_for_resume.py,sha256=yFNpYDJO5mgAhIpMb1i7Epo7ZhO15Ot9RsSUdW8AjUg,2178
324
324
  opentrons/protocol_engine/commands/absorbance_reader/__init__.py,sha256=umS98LlkBEAToRCvxLpr4k43tRPPTfkYu-81-bLy5DU,1251
325
325
  opentrons/protocol_engine/commands/absorbance_reader/close_lid.py,sha256=i6kggql8hJgTxpUbc5dmdMdHDsYWiczfMvZNA72FmvI,5540
326
- opentrons/protocol_engine/commands/absorbance_reader/initialize.py,sha256=HKsgwCfv6LR4SJ06721eq9_NhKcxtVR5lYIdaZPL-_4,4760
326
+ opentrons/protocol_engine/commands/absorbance_reader/initialize.py,sha256=bbs7FwfpGVNAa5UT_rMV7ImrRnmREN9kRhmG8xJsAaU,5373
327
327
  opentrons/protocol_engine/commands/absorbance_reader/open_lid.py,sha256=Ef5coWs4GGOzmZbrAFZXTKq8hZCC7rJc2DVCOU2PFJg,5531
328
328
  opentrons/protocol_engine/commands/absorbance_reader/read.py,sha256=xu5KijW_sSEtjdhNIEctYkQBZC8Baj650W6s8nXfTKw,8187
329
329
  opentrons/protocol_engine/commands/calibration/__init__.py,sha256=JjNnULLBM3j8VtpfHOvH51em9jVLR_ezyrUJUWqxuYI,1611
@@ -362,12 +362,12 @@ opentrons/protocol_engine/commands/unsafe/__init__.py,sha256=lx3TFW_78XK0bdtLFuG
362
362
  opentrons/protocol_engine/commands/unsafe/unsafe_blow_out_in_place.py,sha256=K1idiUT1RW_mWa_SIe3JOUaeuSsUbv12toWgAB1KSvM,3065
363
363
  opentrons/protocol_engine/commands/unsafe/unsafe_drop_tip_in_place.py,sha256=v5h1sLxx_NMAhVCLbmECd7sD0PsecB4Dj-oaBEe0YBs,3635
364
364
  opentrons/protocol_engine/commands/unsafe/unsafe_engage_axes.py,sha256=hCRqFeBInC9Mxz9LqUgIrjTTAjuMEq5MJxCqqXwi3N4,2552
365
- opentrons/protocol_engine/commands/unsafe/unsafe_place_labware.py,sha256=HnYnFViUtPx2p3UXntdMa5LyBLE2AYLjskvnIIWYZ24,7576
365
+ opentrons/protocol_engine/commands/unsafe/unsafe_place_labware.py,sha256=etNdLxj0TtmBA5fTLR-OSfbnRNbXKDzE__aKRAkvWMM,7866
366
366
  opentrons/protocol_engine/commands/unsafe/unsafe_ungrip_labware.py,sha256=MSYftVeLHqnIyX0n9IJLq6XOb7P835EQg7KR3n3PU6c,2397
367
- opentrons/protocol_engine/commands/unsafe/update_position_estimators.py,sha256=pXew4UsDB4Kh7z9r2l7-S3D8bICNmdDr8lNkNEnByYE,2938
368
- opentrons/protocol_engine/errors/__init__.py,sha256=rwpfKoEIZUN7c5kf0T9b0MHCJdDxZiUTCvQs64jPtVI,5174
369
- opentrons/protocol_engine/errors/error_occurrence.py,sha256=PM_bxIxLYKtsRl_cGMiCtXVVMEb88hkLFEWcafwqLf8,7542
370
- opentrons/protocol_engine/errors/exceptions.py,sha256=bLZ5ZYUgUH-CjGtflbDBbmWCqUPr6MGcpE1Yiip3ll4,40153
367
+ opentrons/protocol_engine/commands/unsafe/update_position_estimators.py,sha256=Jgds_cwEHBiiDyJVK6wL34VTWZikrSVYJSRUohnsod8,3044
368
+ opentrons/protocol_engine/errors/__init__.py,sha256=4CEG_WmrdSmESVkSkwfLlbNwW_r8TlMbD_fEXzcn5qg,5232
369
+ opentrons/protocol_engine/errors/error_occurrence.py,sha256=Uoa0tqJ4C-oosDUwk-XXf0NxgWVnVnnhTFOqJAT1J_o,7896
370
+ opentrons/protocol_engine/errors/exceptions.py,sha256=yHLqtkKQbJev80zZYvZz8gjw8luXxWRrcJe3TEcIj88,40604
371
371
  opentrons/protocol_engine/execution/__init__.py,sha256=X8qTYYOc1v84JIDM1xYpIo3VG_BPnsrfl2jm9UUQGwQ,1384
372
372
  opentrons/protocol_engine/execution/command_executor.py,sha256=F1V7H_Y7mqc4hJMfs79aNZ7qG0NFh-xj2BHt4fSUK-4,8202
373
373
  opentrons/protocol_engine/execution/create_queue_worker.py,sha256=6hMuOqbqwfMn63idsrAgro0NJZoyX4VVH7AV3le2EWM,3372
@@ -446,7 +446,7 @@ opentrons/protocol_runner/legacy_command_mapper.py,sha256=N6Ubuz8VHHh0Ga3JgHVhBx
446
446
  opentrons/protocol_runner/legacy_context_plugin.py,sha256=G_qpeyaLvsCjb72_n96Luy8CPSfgPZpt0QKVzKc6LKY,4730
447
447
  opentrons/protocol_runner/protocol_runner.py,sha256=VtgqbcrcKhvWwGyl-LeX-0LjcQw3_frvwyi3d3UlzR8,20732
448
448
  opentrons/protocol_runner/python_protocol_wrappers.py,sha256=2eIGAlKCLfVSUjO5hhd_UKCEBQX_nS4Y9E5wXm2WvhY,6505
449
- opentrons/protocol_runner/run_orchestrator.py,sha256=H_1oryOKooKVmDaKukP9htm0JmgDTJJ6iRs-uEp8jxc,18324
449
+ opentrons/protocol_runner/run_orchestrator.py,sha256=d--Ezeo2M8hEk0Vjp2tZMvJFqaNKFkrQNB24F03DACo,18864
450
450
  opentrons/protocol_runner/task_queue.py,sha256=YH8_lvuLBYjfzXAOJU8DYXizQcbaxGmUiAPmd7kHERw,2581
451
451
  opentrons/protocols/__init__.py,sha256=cOUxilkIvdlqGvN4nYJQYr0TGdIWnzxBaTfoz3svmw8,245
452
452
  opentrons/protocols/bundle.py,sha256=QW_2kwnxgdG_nNPl2e110A5ehOH9Ej63-9TBx-F9Yvw,3666
@@ -485,7 +485,7 @@ opentrons/protocols/models/__init__.py,sha256=KePRAkkKzFoc0lAz8y89cWnxru8ofe3mow
485
485
  opentrons/protocols/models/json_protocol.py,sha256=lteWlIBXgRM86k-wO1dKsx02G2_4kustSDeSoyc5N5U,20128
486
486
  opentrons/protocols/parameters/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
487
487
  opentrons/protocols/parameters/csv_parameter_definition.py,sha256=xZhGtdfH2vXSWFK-aGET07_L0LODvHWkfNxf7pQoOmo,2762
488
- opentrons/protocols/parameters/csv_parameter_interface.py,sha256=S7fdY39TxXqXO-jnaTZFLFI62n0HGc_XjYScWGB44VY,3945
488
+ opentrons/protocols/parameters/csv_parameter_interface.py,sha256=lcELoVQi3PRIDFd4Dw7bi4wODi1tjGuMb2F5X4c4m6Y,4086
489
489
  opentrons/protocols/parameters/exceptions.py,sha256=vQUeyy8Yk_fzP4bvT0r_zu3s7Aty3LM7PzTV6k2iXu0,1092
490
490
  opentrons/protocols/parameters/parameter_definition.py,sha256=OMtUCPKyFx5ZH3Jfcw05aF8pptWQ7XYzYttGMuSPu9k,9529
491
491
  opentrons/protocols/parameters/types.py,sha256=h7vaNmKbDHc1q_FzbZoIgoSVo0mvS64FeiLZDnv7xnQ,489
@@ -512,9 +512,9 @@ opentrons/util/helpers.py,sha256=3hr801bWGbxEcOFAS7f-iOhmnUhoK5qahbB8SIvaCfY,165
512
512
  opentrons/util/linal.py,sha256=IlKAP9HkNBBgULeSf4YVwSKHdx9jnCjSr7nvDvlRALg,5753
513
513
  opentrons/util/logging_config.py,sha256=t3xRxQ5zfXQsU8S4gl6yvrtqx6dxOGyBwIM43CGRyjE,6887
514
514
  opentrons/util/performance_helpers.py,sha256=ew7H8XD20iS6-2TJAzbQeyzStZkkE6PzHt_Adx3wbZQ,5172
515
- opentrons-8.2.0a1.dist-info/LICENSE,sha256=z8d0m5b2O9McPEK1xHG_dWgUBT6EfBDz6wA0F7xSPTA,11358
516
- opentrons-8.2.0a1.dist-info/METADATA,sha256=1X3bpJiJdC2f93wQJUlj-jC0LTujfDRA2Rdrk4895lw,5019
517
- opentrons-8.2.0a1.dist-info/WHEEL,sha256=_4XEmVmaBFWtekSGrbfOGNjC2I5lUr0lZSRblBllIFA,109
518
- opentrons-8.2.0a1.dist-info/entry_points.txt,sha256=fTa6eGCYkvOtv0ov-KVE8LLGetgb35LQLF9x85OWPVw,106
519
- opentrons-8.2.0a1.dist-info/top_level.txt,sha256=wk6whpbMZdBQpcK0Fg0YVfUGrAgVOFON7oQAhOMGMW8,10
520
- opentrons-8.2.0a1.dist-info/RECORD,,
515
+ opentrons-8.2.0a3.dist-info/LICENSE,sha256=z8d0m5b2O9McPEK1xHG_dWgUBT6EfBDz6wA0F7xSPTA,11358
516
+ opentrons-8.2.0a3.dist-info/METADATA,sha256=Hiug_qRDAMEctJZO5vxeTVfBQWl2wEB_OS20oz79Ru0,5019
517
+ opentrons-8.2.0a3.dist-info/WHEEL,sha256=_4XEmVmaBFWtekSGrbfOGNjC2I5lUr0lZSRblBllIFA,109
518
+ opentrons-8.2.0a3.dist-info/entry_points.txt,sha256=fTa6eGCYkvOtv0ov-KVE8LLGetgb35LQLF9x85OWPVw,106
519
+ opentrons-8.2.0a3.dist-info/top_level.txt,sha256=wk6whpbMZdBQpcK0Fg0YVfUGrAgVOFON7oQAhOMGMW8,10
520
+ opentrons-8.2.0a3.dist-info/RECORD,,