opentrons 8.2.0a3__py2.py3-none-any.whl → 8.3.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.
Files changed (27) hide show
  1. opentrons/config/defaults_ot3.py +1 -0
  2. opentrons/hardware_control/backends/flex_protocol.py +13 -1
  3. opentrons/hardware_control/backends/ot3controller.py +36 -4
  4. opentrons/hardware_control/backends/ot3simulator.py +8 -1
  5. opentrons/hardware_control/backends/ot3utils.py +40 -2
  6. opentrons/hardware_control/dev_types.py +2 -0
  7. opentrons/hardware_control/instruments/ot2/pipette.py +6 -1
  8. opentrons/hardware_control/instruments/ot3/pipette.py +10 -0
  9. opentrons/hardware_control/ot3api.py +26 -1
  10. opentrons/protocol_api/core/engine/instrument.py +15 -0
  11. opentrons/protocol_api/core/instrument.py +4 -0
  12. opentrons/protocol_api/core/legacy/legacy_instrument_core.py +3 -0
  13. opentrons/protocol_api/core/legacy_simulator/legacy_instrument_core.py +3 -0
  14. opentrons/protocol_api/instrument_context.py +13 -1
  15. opentrons/protocol_api/validation.py +1 -0
  16. opentrons/protocol_engine/commands/absorbance_reader/read.py +4 -0
  17. opentrons/protocol_engine/commands/liquid_probe.py +8 -0
  18. opentrons/protocol_engine/commands/unsafe/unsafe_ungrip_labware.py +3 -1
  19. opentrons/protocol_engine/resources/pipette_data_provider.py +14 -0
  20. opentrons/protocol_engine/state/modules.py +4 -1
  21. opentrons/protocol_engine/state/pipettes.py +9 -0
  22. {opentrons-8.2.0a3.dist-info → opentrons-8.3.0a0.dist-info}/METADATA +4 -4
  23. {opentrons-8.2.0a3.dist-info → opentrons-8.3.0a0.dist-info}/RECORD +27 -27
  24. {opentrons-8.2.0a3.dist-info → opentrons-8.3.0a0.dist-info}/LICENSE +0 -0
  25. {opentrons-8.2.0a3.dist-info → opentrons-8.3.0a0.dist-info}/WHEEL +0 -0
  26. {opentrons-8.2.0a3.dist-info → opentrons-8.3.0a0.dist-info}/entry_points.txt +0 -0
  27. {opentrons-8.2.0a3.dist-info → opentrons-8.3.0a0.dist-info}/top_level.txt +0 -0
@@ -75,6 +75,7 @@ DEFAULT_RIGHT_MOUNT_OFFSET: Final[Offset] = (40.5, -60.5, 255.675)
75
75
  DEFAULT_GRIPPER_MOUNT_OFFSET: Final[Offset] = (84.55, -12.75, 93.85)
76
76
  DEFAULT_SAFE_HOME_DISTANCE: Final = 5
77
77
  DEFAULT_CALIBRATION_AXIS_MAX_SPEED: Final = 30
78
+ DEFAULT_EMULSIFYING_PIPETTE_AXIS_MAX_SPEED: Final = 90
78
79
 
79
80
  DEFAULT_MAX_SPEEDS: Final[ByGantryLoad[Dict[OT3AxisKind, float]]] = ByGantryLoad(
80
81
  high_throughput={
@@ -60,6 +60,14 @@ class FlexBackend(Protocol):
60
60
  def grab_pressure(self, channels: int, mount: OT3Mount) -> AsyncIterator[None]:
61
61
  ...
62
62
 
63
+ def set_pressure_sensor_available(
64
+ self, pipette_axis: Axis, available: bool
65
+ ) -> None:
66
+ ...
67
+
68
+ def get_pressure_sensor_available(self, pipette_axis: Axis) -> bool:
69
+ ...
70
+
63
71
  def update_constraints_for_gantry_load(self, gantry_load: GantryLoad) -> None:
64
72
  ...
65
73
 
@@ -70,7 +78,11 @@ class FlexBackend(Protocol):
70
78
  ...
71
79
 
72
80
  def update_constraints_for_plunger_acceleration(
73
- self, mount: OT3Mount, acceleration: float, gantry_load: GantryLoad
81
+ self,
82
+ mount: OT3Mount,
83
+ acceleration: float,
84
+ gantry_load: GantryLoad,
85
+ high_speed_pipette: bool = False,
74
86
  ) -> None:
75
87
  ...
76
88
 
@@ -197,6 +197,7 @@ from opentrons_shared_data.errors.exceptions import (
197
197
  PipetteLiquidNotFoundError,
198
198
  CommunicationError,
199
199
  PythonException,
200
+ UnsupportedHardwareCommand,
200
201
  )
201
202
 
202
203
  from .subsystem_manager import SubsystemManager
@@ -362,6 +363,7 @@ class OT3Controller(FlexBackend):
362
363
  self._configuration.motion_settings, GantryLoad.LOW_THROUGHPUT
363
364
  )
364
365
  )
366
+ self._pressure_sensor_available: Dict[NodeId, bool] = {}
365
367
 
366
368
  @asynccontextmanager
367
369
  async def restore_system_constraints(self) -> AsyncIterator[None]:
@@ -380,6 +382,16 @@ class OT3Controller(FlexBackend):
380
382
  async with grab_pressure(channels, tool, self._messenger):
381
383
  yield
382
384
 
385
+ def set_pressure_sensor_available(
386
+ self, pipette_axis: Axis, available: bool
387
+ ) -> None:
388
+ pip_node = axis_to_node(pipette_axis)
389
+ self._pressure_sensor_available[pip_node] = available
390
+
391
+ def get_pressure_sensor_available(self, pipette_axis: Axis) -> bool:
392
+ pip_node = axis_to_node(pipette_axis)
393
+ return self._pressure_sensor_available[pip_node]
394
+
383
395
  def update_constraints_for_calibration_with_gantry_load(
384
396
  self,
385
397
  gantry_load: GantryLoad,
@@ -399,10 +411,18 @@ class OT3Controller(FlexBackend):
399
411
  )
400
412
 
401
413
  def update_constraints_for_plunger_acceleration(
402
- self, mount: OT3Mount, acceleration: float, gantry_load: GantryLoad
414
+ self,
415
+ mount: OT3Mount,
416
+ acceleration: float,
417
+ gantry_load: GantryLoad,
418
+ high_speed_pipette: bool = False,
403
419
  ) -> None:
404
420
  new_constraints = get_system_constraints_for_plunger_acceleration(
405
- self._configuration.motion_settings, gantry_load, mount, acceleration
421
+ self._configuration.motion_settings,
422
+ gantry_load,
423
+ mount,
424
+ acceleration,
425
+ high_speed_pipette,
406
426
  )
407
427
  self._move_manager.update_constraints(new_constraints)
408
428
 
@@ -679,7 +699,8 @@ class OT3Controller(FlexBackend):
679
699
 
680
700
  pipettes_moving = moving_pipettes_in_move_group(move_group)
681
701
 
682
- async with self._monitor_overpressure(pipettes_moving):
702
+ checked_moving_pipettes = self._pipettes_to_monitor_pressure(pipettes_moving)
703
+ async with self._monitor_overpressure(checked_moving_pipettes):
683
704
  positions = await runner.run(can_messenger=self._messenger)
684
705
  self._handle_motor_status_response(positions)
685
706
 
@@ -786,7 +807,8 @@ class OT3Controller(FlexBackend):
786
807
  moving_pipettes = [
787
808
  axis_to_node(ax) for ax in checked_axes if ax in Axis.pipette_axes()
788
809
  ]
789
- async with self._monitor_overpressure(moving_pipettes):
810
+ checked_moving_pipettes = self._pipettes_to_monitor_pressure(moving_pipettes)
811
+ async with self._monitor_overpressure(checked_moving_pipettes):
790
812
  positions = await asyncio.gather(*coros)
791
813
  # TODO(CM): default gear motor homing routine to have some acceleration
792
814
  if Axis.Q in checked_axes:
@@ -800,6 +822,9 @@ class OT3Controller(FlexBackend):
800
822
  self._handle_motor_status_response(position)
801
823
  return axis_convert(self._position, 0.0)
802
824
 
825
+ def _pipettes_to_monitor_pressure(self, pipettes: List[NodeId]) -> List[NodeId]:
826
+ return [pip for pip in pipettes if self._pressure_sensor_available[pip]]
827
+
803
828
  def _filter_move_group(self, move_group: MoveGroup) -> MoveGroup:
804
829
  new_group: MoveGroup = []
805
830
  for step in move_group:
@@ -915,6 +940,7 @@ class OT3Controller(FlexBackend):
915
940
  lookup_name = {
916
941
  FirmwarePipetteName.p1000_single: "P1KS",
917
942
  FirmwarePipetteName.p1000_multi: "P1KM",
943
+ FirmwarePipetteName.p1000_multi_em: "P1KP",
918
944
  FirmwarePipetteName.p50_single: "P50S",
919
945
  FirmwarePipetteName.p50_multi: "P50M",
920
946
  FirmwarePipetteName.p1000_96: "P1KH",
@@ -949,6 +975,7 @@ class OT3Controller(FlexBackend):
949
975
  converted_name.pipette_type,
950
976
  converted_name.pipette_channels,
951
977
  converted_name.pipette_version,
978
+ converted_name.oem_type,
952
979
  ),
953
980
  "id": OT3Controller._combine_serial_number(attached),
954
981
  }
@@ -1378,6 +1405,11 @@ class OT3Controller(FlexBackend):
1378
1405
  ) -> float:
1379
1406
  head_node = axis_to_node(Axis.by_mount(mount))
1380
1407
  tool = sensor_node_for_pipette(OT3Mount(mount.value))
1408
+ if tool not in self._pipettes_to_monitor_pressure([tool]):
1409
+ raise UnsupportedHardwareCommand(
1410
+ "Liquid Presence Detection not available on this pipette."
1411
+ )
1412
+
1381
1413
  positions = await liquid_probe(
1382
1414
  messenger=self._messenger,
1383
1415
  tool=tool,
@@ -235,7 +235,11 @@ class OT3Simulator(FlexBackend):
235
235
  self._sim_gantry_load = gantry_load
236
236
 
237
237
  def update_constraints_for_plunger_acceleration(
238
- self, mount: OT3Mount, acceleration: float, gantry_load: GantryLoad
238
+ self,
239
+ mount: OT3Mount,
240
+ acceleration: float,
241
+ gantry_load: GantryLoad,
242
+ high_speed_pipette: bool = False,
239
243
  ) -> None:
240
244
  self._sim_gantry_load = gantry_load
241
245
 
@@ -505,6 +509,7 @@ class OT3Simulator(FlexBackend):
505
509
  converted_name.pipette_type,
506
510
  converted_name.pipette_channels,
507
511
  converted_name.pipette_version,
512
+ converted_name.oem_type,
508
513
  ),
509
514
  "id": None,
510
515
  }
@@ -527,6 +532,7 @@ class OT3Simulator(FlexBackend):
527
532
  converted_name.pipette_type,
528
533
  converted_name.pipette_channels,
529
534
  converted_name.pipette_version,
535
+ converted_name.oem_type,
530
536
  ),
531
537
  "id": init_instr["id"],
532
538
  }
@@ -538,6 +544,7 @@ class OT3Simulator(FlexBackend):
538
544
  converted_name.pipette_type,
539
545
  converted_name.pipette_channels,
540
546
  converted_name.pipette_version,
547
+ converted_name.oem_type,
541
548
  ),
542
549
  "id": None,
543
550
  }
@@ -2,7 +2,10 @@
2
2
  from typing import Dict, Iterable, List, Set, Tuple, TypeVar, cast, Sequence, Optional
3
3
  from typing_extensions import Literal
4
4
  from logging import getLogger
5
- from opentrons.config.defaults_ot3 import DEFAULT_CALIBRATION_AXIS_MAX_SPEED
5
+ from opentrons.config.defaults_ot3 import (
6
+ DEFAULT_CALIBRATION_AXIS_MAX_SPEED,
7
+ DEFAULT_EMULSIFYING_PIPETTE_AXIS_MAX_SPEED,
8
+ )
6
9
  from opentrons.config.types import OT3MotionSettings, OT3CurrentSettings, GantryLoad
7
10
  from opentrons.hardware_control.types import (
8
11
  Axis,
@@ -281,12 +284,22 @@ def get_system_constraints_for_plunger_acceleration(
281
284
  gantry_load: GantryLoad,
282
285
  mount: OT3Mount,
283
286
  acceleration: float,
287
+ high_speed_pipette: bool = False,
284
288
  ) -> "SystemConstraints[Axis]":
285
289
  old_constraints = config.by_gantry_load(gantry_load)
286
290
  new_constraints = {}
287
291
  axis_kinds = set([k for _, v in old_constraints.items() for k in v.keys()])
292
+
293
+ def _get_axis_max_speed(ax: Axis) -> float:
294
+ if ax == Axis.of_main_tool_actuator(mount) and high_speed_pipette:
295
+ _max_speed = float(DEFAULT_EMULSIFYING_PIPETTE_AXIS_MAX_SPEED)
296
+ else:
297
+ _max_speed = old_constraints["default_max_speed"][axis_kind]
298
+ return _max_speed
299
+
288
300
  for axis_kind in axis_kinds:
289
301
  for axis in Axis.of_kind(axis_kind):
302
+ _default_max_speed = _get_axis_max_speed(axis)
290
303
  if axis == Axis.of_main_tool_actuator(mount):
291
304
  _accel = acceleration
292
305
  else:
@@ -295,7 +308,32 @@ def get_system_constraints_for_plunger_acceleration(
295
308
  _accel,
296
309
  old_constraints["max_speed_discontinuity"][axis_kind],
297
310
  old_constraints["direction_change_speed_discontinuity"][axis_kind],
298
- old_constraints["default_max_speed"][axis_kind],
311
+ _default_max_speed,
312
+ )
313
+ return new_constraints
314
+
315
+
316
+ def get_system_constraints_for_emulsifying_pipette(
317
+ config: OT3MotionSettings,
318
+ gantry_load: GantryLoad,
319
+ mount: OT3Mount,
320
+ ) -> "SystemConstraints[Axis]":
321
+ old_constraints = config.by_gantry_load(gantry_load)
322
+ new_constraints = {}
323
+ axis_kinds = set([k for _, v in old_constraints.items() for k in v.keys()])
324
+ for axis_kind in axis_kinds:
325
+ for axis in Axis.of_kind(axis_kind):
326
+ if axis == Axis.of_main_tool_actuator(mount):
327
+ _max_speed = float(DEFAULT_EMULSIFYING_PIPETTE_AXIS_MAX_SPEED)
328
+ else:
329
+ _max_speed = old_constraints["default_max_speed"][axis_kind]
330
+ new_constraints[axis] = AxisConstraints.build(
331
+ max_acceleration=old_constraints["acceleration"][axis_kind],
332
+ max_speed_discont=old_constraints["max_speed_discontinuity"][axis_kind],
333
+ max_direction_change_speed_discont=old_constraints[
334
+ "direction_change_speed_discontinuity"
335
+ ][axis_kind],
336
+ max_speed=_max_speed,
299
337
  )
300
338
  return new_constraints
301
339
 
@@ -20,6 +20,7 @@ from opentrons_shared_data.pipette.pipette_definition import (
20
20
  PipetteConfigurations,
21
21
  SupportedTipsDefinition,
22
22
  PipetteBoundingBoxOffsetDefinition,
23
+ AvailableSensorDefinition,
23
24
  )
24
25
  from opentrons_shared_data.gripper import (
25
26
  GripperModel,
@@ -100,6 +101,7 @@ class PipetteDict(InstrumentDict):
100
101
  pipette_bounding_box_offsets: PipetteBoundingBoxOffsetDefinition
101
102
  current_nozzle_map: NozzleMap
102
103
  lld_settings: Optional[Dict[str, Dict[str, float]]]
104
+ available_sensors: AvailableSensorDefinition
103
105
 
104
106
 
105
107
  class PipetteStateDict(TypedDict):
@@ -56,6 +56,7 @@ from opentrons_shared_data.pipette.types import (
56
56
  UlPerMmAction,
57
57
  PipetteName,
58
58
  PipetteModel,
59
+ PipetteOEMType,
59
60
  )
60
61
  from opentrons.hardware_control.dev_types import InstrumentHardwareConfigs
61
62
 
@@ -112,17 +113,20 @@ class Pipette(AbstractInstrument[PipetteConfigurations]):
112
113
  pipette_type=config.pipette_type,
113
114
  pipette_channels=config.channels,
114
115
  pipette_generation=config.display_category,
116
+ oem_type=PipetteOEMType.OT,
115
117
  )
116
118
  self._acting_as = self._pipette_name
117
119
  self._pipette_model = PipetteModelVersionType(
118
120
  pipette_type=config.pipette_type,
119
121
  pipette_channels=config.channels,
120
122
  pipette_version=config.version,
123
+ oem_type=PipetteOEMType.OT,
121
124
  )
122
125
  self._valid_nozzle_maps = load_pipette_data.load_valid_nozzle_maps(
123
126
  self._pipette_model.pipette_type,
124
127
  self._pipette_model.pipette_channels,
125
128
  self._pipette_model.pipette_version,
129
+ PipetteOEMType.OT,
126
130
  )
127
131
  self._nozzle_offset = self._config.nozzle_offset
128
132
  self._nozzle_manager = (
@@ -189,7 +193,7 @@ class Pipette(AbstractInstrument[PipetteConfigurations]):
189
193
  ], f"{self.name} is not back-compatible with {name}"
190
194
 
191
195
  liquid_model = load_pipette_data.load_liquid_model(
192
- name.pipette_type, name.pipette_channels, name.get_version()
196
+ name.pipette_type, name.pipette_channels, name.get_version(), name.oem_type
193
197
  )
194
198
  # TODO need to grab name config here to deal with act as test
195
199
  self._liquid_class.max_volume = liquid_model["default"].max_volume
@@ -280,6 +284,7 @@ class Pipette(AbstractInstrument[PipetteConfigurations]):
280
284
  self._pipette_model.pipette_type,
281
285
  self._pipette_model.pipette_channels,
282
286
  self._pipette_model.pipette_version,
287
+ self._pipette_model.oem_type,
283
288
  )
284
289
  self._config_as_dict = self._config.dict()
285
290
 
@@ -41,6 +41,8 @@ from opentrons_shared_data.pipette.types import (
41
41
  UlPerMmAction,
42
42
  PipetteName,
43
43
  PipetteModel,
44
+ Quirks,
45
+ PipetteOEMType,
44
46
  )
45
47
  from opentrons_shared_data.pipette import (
46
48
  load_data as load_pipette_data,
@@ -92,22 +94,26 @@ class Pipette(AbstractInstrument[PipetteConfigurations]):
92
94
  self._liquid_class_name = pip_types.LiquidClasses.default
93
95
  self._liquid_class = self._config.liquid_properties[self._liquid_class_name]
94
96
 
97
+ oem = PipetteOEMType.get_oem_from_quirks(config.quirks)
95
98
  # TODO (lc 12-05-2022) figure out how we can safely deprecate "name" and "model"
96
99
  self._pipette_name = PipetteNameType(
97
100
  pipette_type=config.pipette_type,
98
101
  pipette_channels=config.channels,
99
102
  pipette_generation=config.display_category,
103
+ oem_type=oem,
100
104
  )
101
105
  self._acting_as = self._pipette_name
102
106
  self._pipette_model = PipetteModelVersionType(
103
107
  pipette_type=config.pipette_type,
104
108
  pipette_channels=config.channels,
105
109
  pipette_version=config.version,
110
+ oem_type=oem,
106
111
  )
107
112
  self._valid_nozzle_maps = load_pipette_data.load_valid_nozzle_maps(
108
113
  self._pipette_model.pipette_type,
109
114
  self._pipette_model.pipette_channels,
110
115
  self._pipette_model.pipette_version,
116
+ self._pipette_model.oem_type,
111
117
  )
112
118
  self._nozzle_offset = self._config.nozzle_offset
113
119
  self._nozzle_manager = (
@@ -225,6 +231,9 @@ class Pipette(AbstractInstrument[PipetteConfigurations]):
225
231
  def push_out_volume(self) -> float:
226
232
  return self._active_tip_settings.default_push_out_volume
227
233
 
234
+ def is_high_speed_pipette(self) -> bool:
235
+ return Quirks.highSpeed in self._config.quirks
236
+
228
237
  def act_as(self, name: PipetteName) -> None:
229
238
  """Reconfigure to act as ``name``. ``name`` must be either the
230
239
  actual name of the pipette, or a name in its back-compatibility
@@ -246,6 +255,7 @@ class Pipette(AbstractInstrument[PipetteConfigurations]):
246
255
  self._pipette_model.pipette_type,
247
256
  self._pipette_model.pipette_channels,
248
257
  self._pipette_model.pipette_version,
258
+ self._pipette_model.oem_type,
249
259
  )
250
260
  self._config_as_dict = self._config.dict()
251
261
 
@@ -32,6 +32,7 @@ from opentrons_shared_data.pipette.types import (
32
32
  )
33
33
  from opentrons_shared_data.pipette import (
34
34
  pipette_load_name_conversions as pipette_load_name,
35
+ pipette_definition,
35
36
  )
36
37
  from opentrons_shared_data.robot.types import RobotType
37
38
 
@@ -299,8 +300,11 @@ class OT3API(
299
300
  async def set_system_constraints_for_plunger_acceleration(
300
301
  self, mount: OT3Mount, acceleration: float
301
302
  ) -> None:
303
+ high_speed_pipette = self._pipette_handler.get_pipette(
304
+ mount
305
+ ).is_high_speed_pipette()
302
306
  self._backend.update_constraints_for_plunger_acceleration(
303
- mount, acceleration, self._gantry_load
307
+ mount, acceleration, self._gantry_load, high_speed_pipette
304
308
  )
305
309
 
306
310
  @contextlib.asynccontextmanager
@@ -633,10 +637,31 @@ class OT3API(
633
637
  self._feature_flags.use_old_aspiration_functions,
634
638
  )
635
639
  self._pipette_handler.hardware_instruments[mount] = p
640
+
641
+ if config is not None:
642
+ self._set_pressure_sensor_available(mount, instrument_config=config)
643
+
636
644
  # TODO (lc 12-5-2022) Properly support backwards compatibility
637
645
  # when applicable
638
646
  return skipped
639
647
 
648
+ def get_pressure_sensor_available(self, mount: OT3Mount) -> bool:
649
+ pip_axis = Axis.of_main_tool_actuator(mount)
650
+ return self._backend.get_pressure_sensor_available(pip_axis)
651
+
652
+ def _set_pressure_sensor_available(
653
+ self,
654
+ mount: OT3Mount,
655
+ instrument_config: pipette_definition.PipetteConfigurations,
656
+ ) -> None:
657
+ pressure_sensor_available = (
658
+ "pressure" in instrument_config.available_sensors.sensors
659
+ )
660
+ pip_axis = Axis.of_main_tool_actuator(mount)
661
+ self._backend.set_pressure_sensor_available(
662
+ pipette_axis=pip_axis, available=pressure_sensor_available
663
+ )
664
+
640
665
  async def cache_gripper(self, instrument_data: AttachedGripper) -> bool:
641
666
  """Set up gripper based on scanned information."""
642
667
  grip_cal = load_gripper_calibration_offset(instrument_data.get("id"))
@@ -31,6 +31,9 @@ from opentrons.protocol_engine.errors.exceptions import TipNotAttachedError
31
31
  from opentrons.protocol_engine.clients import SyncClient as EngineClient
32
32
  from opentrons.protocols.api_support.definitions import MAX_SUPPORTED_VERSION
33
33
  from opentrons_shared_data.pipette.types import PipetteNameType
34
+ from opentrons_shared_data.errors.exceptions import (
35
+ UnsupportedHardwareCommand,
36
+ )
34
37
  from opentrons.protocol_api._nozzle_layout import NozzleLayout
35
38
  from opentrons.hardware_control.nozzle_manager import NozzleConfigurationType
36
39
  from opentrons.hardware_control.nozzle_manager import NozzleMap
@@ -86,6 +89,13 @@ class InstrumentCore(AbstractInstrument[WellCore]):
86
89
  self._liquid_presence_detection = bool(
87
90
  self._engine_client.state.pipettes.get_liquid_presence_detection(pipette_id)
88
91
  )
92
+ if (
93
+ self._liquid_presence_detection
94
+ and not self._pressure_supported_by_pipette()
95
+ ):
96
+ raise UnsupportedHardwareCommand(
97
+ "Pressure sensor not available for this pipette"
98
+ )
89
99
 
90
100
  @property
91
101
  def pipette_id(self) -> str:
@@ -847,6 +857,11 @@ class InstrumentCore(AbstractInstrument[WellCore]):
847
857
  z_axis = self._engine_client.state.pipettes.get_z_axis(self._pipette_id)
848
858
  self._engine_client.execute_command(cmd.HomeParams(axes=[z_axis]))
849
859
 
860
+ def _pressure_supported_by_pipette(self) -> bool:
861
+ return self._engine_client.state.pipettes.get_pipette_supports_pressure(
862
+ self.pipette_id
863
+ )
864
+
850
865
  def detect_liquid_presence(self, well_core: WellCore, loc: Location) -> bool:
851
866
  labware_id = well_core.labware_id
852
867
  well_name = well_core.get_name()
@@ -253,6 +253,10 @@ class AbstractInstrument(ABC, Generic[WellCoreType]):
253
253
  def get_liquid_presence_detection(self) -> bool:
254
254
  ...
255
255
 
256
+ @abstractmethod
257
+ def _pressure_supported_by_pipette(self) -> bool:
258
+ ...
259
+
256
260
  @abstractmethod
257
261
  def set_liquid_presence_detection(self, enable: bool) -> None:
258
262
  ...
@@ -583,3 +583,6 @@ class LegacyInstrumentCore(AbstractInstrument[LegacyWellCore]):
583
583
  ) -> float:
584
584
  """This will never be called because it was added in API 2.20."""
585
585
  assert False, "liquid_probe_without_recovery only supported in API 2.20 & later"
586
+
587
+ def _pressure_supported_by_pipette(self) -> bool:
588
+ return False
@@ -501,3 +501,6 @@ class LegacyInstrumentCoreSimulator(AbstractInstrument[LegacyWellCore]):
501
501
  ) -> float:
502
502
  """This will never be called because it was added in API 2.20."""
503
503
  assert False, "liquid_probe_without_recovery only supported in API 2.20 & later"
504
+
505
+ def _pressure_supported_by_pipette(self) -> bool:
506
+ return False
@@ -7,6 +7,7 @@ from opentrons_shared_data.errors.exceptions import (
7
7
  CommandPreconditionViolated,
8
8
  CommandParameterLimitViolated,
9
9
  UnexpectedTipRemovalError,
10
+ UnsupportedHardwareCommand,
10
11
  )
11
12
  from opentrons.legacy_broker import LegacyBroker
12
13
  from opentrons.hardware_control.dev_types import PipetteDict
@@ -260,6 +261,7 @@ class InstrumentContext(publisher.CommandPublisher):
260
261
  and self._96_tip_config_valid()
261
262
  and self._core.get_current_volume() == 0
262
263
  ):
264
+ self._raise_if_pressure_not_supported_by_pipette()
263
265
  self.require_liquid_presence(well=well)
264
266
 
265
267
  with publisher.publish_context(
@@ -1694,6 +1696,8 @@ class InstrumentContext(publisher.CommandPublisher):
1694
1696
  @liquid_presence_detection.setter
1695
1697
  @requires_version(2, 20)
1696
1698
  def liquid_presence_detection(self, enable: bool) -> None:
1699
+ if enable:
1700
+ self._raise_if_pressure_not_supported_by_pipette()
1697
1701
  self._core.set_liquid_presence_detection(enable)
1698
1702
 
1699
1703
  @property
@@ -2143,6 +2147,7 @@ class InstrumentContext(publisher.CommandPublisher):
2143
2147
  .. note::
2144
2148
  The pressure sensors for the Flex 8-channel pipette are on channels 1 and 8 (positions A1 and H1). For the Flex 96-channel pipette, the pressure sensors are on channels 1 and 96 (positions A1 and H12). Other channels on multi-channel pipettes do not have sensors and cannot detect liquid.
2145
2149
  """
2150
+ self._raise_if_pressure_not_supported_by_pipette()
2146
2151
  loc = well.top()
2147
2152
  self._96_tip_config_valid()
2148
2153
  return self._core.detect_liquid_presence(well._core, loc)
@@ -2156,6 +2161,7 @@ class InstrumentContext(publisher.CommandPublisher):
2156
2161
  .. note::
2157
2162
  The pressure sensors for the Flex 8-channel pipette are on channels 1 and 8 (positions A1 and H1). For the Flex 96-channel pipette, the pressure sensors are on channels 1 and 96 (positions A1 and H12). Other channels on multi-channel pipettes do not have sensors and cannot detect liquid.
2158
2163
  """
2164
+ self._raise_if_pressure_not_supported_by_pipette()
2159
2165
  loc = well.top()
2160
2166
  self._96_tip_config_valid()
2161
2167
  self._core.liquid_probe_with_recovery(well._core, loc)
@@ -2170,7 +2176,7 @@ class InstrumentContext(publisher.CommandPublisher):
2170
2176
 
2171
2177
  This is intended for Opentrons internal use only and is not a guaranteed API.
2172
2178
  """
2173
-
2179
+ self._raise_if_pressure_not_supported_by_pipette()
2174
2180
  loc = well.top()
2175
2181
  self._96_tip_config_valid()
2176
2182
  height = self._core.liquid_probe_without_recovery(well._core, loc)
@@ -2192,6 +2198,12 @@ class InstrumentContext(publisher.CommandPublisher):
2192
2198
  )
2193
2199
  # SINGLE, QUADRANT and ALL are supported by all pipettes
2194
2200
 
2201
+ def _raise_if_pressure_not_supported_by_pipette(self) -> None:
2202
+ if not self._core._pressure_supported_by_pipette():
2203
+ raise UnsupportedHardwareCommand(
2204
+ "Pressure sensor not available for this pipette"
2205
+ )
2206
+
2195
2207
  def _handle_aspirate_target(
2196
2208
  self, target: validation.ValidTarget
2197
2209
  ) -> tuple[types.Location, Optional[labware.Well], Optional[bool]]:
@@ -63,6 +63,7 @@ _PIPETTE_NAMES_MAP = {
63
63
  "flex_8channel_50": PipetteNameType.P50_MULTI_FLEX,
64
64
  "flex_1channel_1000": PipetteNameType.P1000_SINGLE_FLEX,
65
65
  "flex_8channel_1000": PipetteNameType.P1000_MULTI_FLEX,
66
+ "flex_8channel_1000_em": PipetteNameType.P1000_MULTI_EM,
66
67
  "flex_96channel_1000": PipetteNameType.P1000_96,
67
68
  }
68
69
 
@@ -80,6 +80,10 @@ 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
+ )
83
87
 
84
88
  # TODO: we need to return a file ID and increase the file count even when a moduel is not attached
85
89
  if (
@@ -16,6 +16,7 @@ from opentrons.protocol_engine.errors.exceptions import (
16
16
  from opentrons.types import MountType
17
17
  from opentrons_shared_data.errors.exceptions import (
18
18
  PipetteLiquidNotFoundError,
19
+ UnsupportedHardwareCommand,
19
20
  )
20
21
 
21
22
  from ..types import DeckPoint
@@ -113,6 +114,13 @@ async def _execute_common(
113
114
  well_name = params.wellName
114
115
 
115
116
  state_update = update_types.StateUpdate()
117
+ if (
118
+ "pressure"
119
+ not in state_view.pipettes.get_config(pipette_id).available_sensors.sensors
120
+ ):
121
+ raise UnsupportedHardwareCommand(
122
+ "Pressure sensor not available for this pipette"
123
+ )
116
124
 
117
125
  # May raise TipNotAttachedError.
118
126
  aspirated_volume = state_view.pipettes.get_aspirated_volume(pipette_id)
@@ -1,6 +1,8 @@
1
1
  """Ungrip labware payload, result, and implementaiton."""
2
2
 
3
3
  from __future__ import annotations
4
+
5
+ from opentrons.hardware_control.types import Axis
4
6
  from opentrons.protocol_engine.errors.exceptions import GripperNotAttachedError
5
7
  from pydantic import BaseModel
6
8
  from typing import Optional, Type
@@ -46,7 +48,7 @@ class UnsafeUngripLabwareImplementation(
46
48
  ot3_hardware_api = ensure_ot3_hardware(self._hardware_api)
47
49
  if not ot3_hardware_api.has_gripper():
48
50
  raise GripperNotAttachedError("No gripper found to perform ungrip.")
49
- await ot3_hardware_api.ungrip()
51
+ await ot3_hardware_api.home([Axis.G])
50
52
  return SuccessData(
51
53
  public=UnsafeUngripLabwareResult(),
52
54
  )
@@ -67,6 +67,7 @@ class LoadedStaticPipetteData:
67
67
  back_left_corner_offset: Point
68
68
  front_right_corner_offset: Point
69
69
  pipette_lld_settings: Optional[Dict[str, Dict[str, float]]]
70
+ available_sensors: pipette_definition.AvailableSensorDefinition
70
71
 
71
72
 
72
73
  class VirtualPipetteDataProvider:
@@ -95,6 +96,7 @@ class VirtualPipetteDataProvider:
95
96
  config.pipette_type,
96
97
  config.channels,
97
98
  config.version,
99
+ pip_types.PipetteOEMType.OT,
98
100
  )
99
101
  new_nozzle_manager = NozzleConfigurationManager.build_from_config(
100
102
  config, valid_nozzle_maps
@@ -127,6 +129,7 @@ class VirtualPipetteDataProvider:
127
129
  pipette_model.pipette_type,
128
130
  pipette_model.pipette_channels,
129
131
  pipette_model.pipette_version,
132
+ pipette_model.oem_type,
130
133
  )
131
134
 
132
135
  liquid_class = pipette_definition.liquid_class_for_volume_between_default_and_defaultlowvolume(
@@ -160,6 +163,7 @@ class VirtualPipetteDataProvider:
160
163
  pipette_model.pipette_type,
161
164
  pipette_model.pipette_channels,
162
165
  pipette_model.pipette_version,
166
+ pipette_model.oem_type,
163
167
  )
164
168
 
165
169
  def _get_virtual_pipette_static_config_by_model( # noqa: C901
@@ -176,6 +180,7 @@ class VirtualPipetteDataProvider:
176
180
  pipette_model.pipette_type,
177
181
  pipette_model.pipette_channels,
178
182
  pipette_model.pipette_version,
183
+ pipette_model.oem_type,
179
184
  )
180
185
  try:
181
186
  tip_type = pip_types.PipetteTipType(
@@ -192,6 +197,7 @@ class VirtualPipetteDataProvider:
192
197
  pipette_model.pipette_type,
193
198
  pipette_model.pipette_channels,
194
199
  pipette_model.pipette_version,
200
+ pipette_model.oem_type,
195
201
  )
196
202
  if pipette_id not in self._nozzle_manager_layout_by_id:
197
203
  nozzle_manager = NozzleConfigurationManager.build_from_config(
@@ -280,6 +286,8 @@ class VirtualPipetteDataProvider:
280
286
  pip_front_right[0], pip_front_right[1], pip_front_right[2]
281
287
  ),
282
288
  pipette_lld_settings=config.lld_settings,
289
+ available_sensors=config.available_sensors
290
+ or pipette_definition.AvailableSensorDefinition(sensors=[]),
283
291
  )
284
292
 
285
293
  def get_virtual_pipette_static_config(
@@ -298,6 +306,11 @@ def get_pipette_static_config(
298
306
  """Get the config for a pipette, given the state/config object from the HW API."""
299
307
  back_left_offset = pipette_dict["pipette_bounding_box_offsets"].back_left_corner
300
308
  front_right_offset = pipette_dict["pipette_bounding_box_offsets"].front_right_corner
309
+ available_sensors = (
310
+ pipette_dict["available_sensors"]
311
+ if "available_sensors" in pipette_dict.keys()
312
+ else pipette_definition.AvailableSensorDefinition(sensors=[])
313
+ )
301
314
  return LoadedStaticPipetteData(
302
315
  model=pipette_dict["model"],
303
316
  display_name=pipette_dict["display_name"],
@@ -327,6 +340,7 @@ def get_pipette_static_config(
327
340
  front_right_offset[0], front_right_offset[1], front_right_offset[2]
328
341
  ),
329
342
  pipette_lld_settings=pipette_dict["lld_settings"],
343
+ available_sensors=available_sensors,
330
344
  )
331
345
 
332
346
 
@@ -1268,7 +1268,10 @@ class ModuleView(HasState[ModuleState]):
1268
1268
  row = chr(ord("A") + i // 12) # Convert index to row (A-H)
1269
1269
  col = (i % 12) + 1 # Convert index to column (1-12)
1270
1270
  well_key = f"{row}{col}"
1271
- well_map[well_key] = value
1271
+ truncated_value = float(
1272
+ "{:.5}".format(str(value))
1273
+ ) # Truncate the returned value to the third decimal place
1274
+ well_map[well_key] = truncated_value
1272
1275
  return well_map
1273
1276
  else:
1274
1277
  raise ValueError(
@@ -98,6 +98,7 @@ class StaticPipetteConfig:
98
98
  bounding_nozzle_offsets: BoundingNozzlesOffsets
99
99
  default_nozzle_map: NozzleMap # todo(mm, 2024-10-14): unused, remove?
100
100
  lld_settings: Optional[Dict[str, Dict[str, float]]]
101
+ available_sensors: pipette_definition.AvailableSensorDefinition
101
102
 
102
103
 
103
104
  @dataclasses.dataclass
@@ -292,6 +293,7 @@ class PipetteStore(HasState[PipetteState], HandlesActions):
292
293
  ),
293
294
  default_nozzle_map=config.nozzle_map,
294
295
  lld_settings=config.pipette_lld_settings,
296
+ available_sensors=config.available_sensors,
295
297
  )
296
298
  self._state.flow_rates_by_id[
297
299
  state_update.pipette_config.pipette_id
@@ -723,6 +725,13 @@ class PipetteView(HasState[PipetteState]):
723
725
  pip_front_left_bound,
724
726
  )
725
727
 
728
+ def get_pipette_supports_pressure(self, pipette_id: str) -> bool:
729
+ """Return if this pipette supports a pressure sensor."""
730
+ return (
731
+ "pressure"
732
+ in self._state.static_config_by_id[pipette_id].available_sensors.sensors
733
+ )
734
+
726
735
  def get_liquid_presence_detection(self, pipette_id: str) -> bool:
727
736
  """Determine if liquid presence detection is enabled for this pipette."""
728
737
  try:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: opentrons
3
- Version: 8.2.0a3
3
+ Version: 8.3.0a0
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.0a3
24
+ Requires-Dist: opentrons-shared-data ==8.3.0a0
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.0a3 ; extra == 'flex-hardware'
37
+ Requires-Dist: opentrons-hardware[flex] ==8.3.0a0 ; extra == 'flex-hardware'
38
38
  Provides-Extra: ot2-hardware
39
- Requires-Dist: opentrons-hardware ==8.2.0a3 ; extra == 'ot2-hardware'
39
+ Requires-Dist: opentrons-hardware ==8.3.0a0 ; extra == 'ot2-hardware'
40
40
 
41
41
  .. _Full API Documentation: http://docs.opentrons.com
42
42
 
@@ -34,7 +34,7 @@ opentrons/cli/analyze.py,sha256=mKDVmrGgXtAHNN8im5XI-WGqbbXf7xeWN5tIqRgOcNo,1435
34
34
  opentrons/config/__init__.py,sha256=B4kGZSsy6ESki_-K33sd0mRNrN_h2_bcZL8zEDOxHEg,21251
35
35
  opentrons/config/advanced_settings.py,sha256=TAnzrGuZhSCokYii2efgW6SeJuAST2xyxO7hNlFxVUk,26560
36
36
  opentrons/config/defaults_ot2.py,sha256=_l63QNW0aWTh0HGZcgF592ETJg8-W4M0XrQbbzkAPjA,6031
37
- opentrons/config/defaults_ot3.py,sha256=ZRmf4n8dor0AghqyeURIJ-uqIIqks_ZAo80IhfI_g2w,14695
37
+ opentrons/config/defaults_ot3.py,sha256=T2NlloDzdwbZBM4Mev2cj7BWcJQbbYOb2OPonvi2gfc,14750
38
38
  opentrons/config/feature_flags.py,sha256=aDWEOEQn9qQ0JcPpIgKq7IHLegbt2d7bMKSkt_5DbKo,2511
39
39
  opentrons/config/gripper_config.py,sha256=ch3PtyP96KOCQi6otf4U6Okt04JrEYhqpQM0SkmP_cA,1721
40
40
  opentrons/config/reset.py,sha256=5NNCfdUfV9_9CVLuXS8Ita1hrCbHbaSwZ7VnmyjBOEQ,6407
@@ -93,14 +93,14 @@ opentrons/hardware_control/__main__.py,sha256=DnlYdphZKLHiLCeXmnMDer6CclhberfTk_
93
93
  opentrons/hardware_control/adapters.py,sha256=mm-gumoaF7CFrP0ad6W4xafbJRq6RaOD0GffSibWR-Q,3888
94
94
  opentrons/hardware_control/api.py,sha256=Lv-Agio_nQ3axGjMbyXckkQRkl9rYfOV8L_Ul8bXGmY,50909
95
95
  opentrons/hardware_control/constants.py,sha256=Ku-XABo7AN9JQ0tef8oPk-JlUuhQQLeP9S0h7kx5GEA,227
96
- opentrons/hardware_control/dev_types.py,sha256=2344pCGVZS7Ap8Ifnei7Lsz2FTpuCkR_o98xXplTs6Y,3299
96
+ opentrons/hardware_control/dev_types.py,sha256=P9AVjZ4yBN-XLOzRjoB3gS2A3MZ7dZRomo77K40cGbk,3379
97
97
  opentrons/hardware_control/errors.py,sha256=NMF5_AvX22ENTHPpUlElgF-0aeaxEhYXnOq2lfIzuiM,1433
98
98
  opentrons/hardware_control/execution_manager.py,sha256=WG3NF1OJgorAmRpkx8BRnqFeQsPUQ5w7H1kVK7pC-7A,5984
99
99
  opentrons/hardware_control/module_control.py,sha256=u0RpbOgr5pV5exgKg3qvNQdwdVuy_9QkPpMZtWthsm8,12074
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=CMtyrIDYzw-H-FQuUlnjVpGEOA9YNWETFy7jDxT8zYk,118073
103
+ opentrons/hardware_control/ot3api.py,sha256=i8Ei3rYwESkkLagdhnqV7dALkv1lDrv9SmJqukw4gIg,119009
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
@@ -113,10 +113,10 @@ opentrons/hardware_control/backends/__init__.py,sha256=u5Dg3AFZuvDV7hFqJ8I4F9D1d
113
113
  opentrons/hardware_control/backends/controller.py,sha256=MOVMq9s1rY_jHhajHB1hQ1MgXXyY54-gMrrvAuMsOFg,14622
114
114
  opentrons/hardware_control/backends/errors.py,sha256=ZiVP16exHMTWWOajxffnXEqI6NNfeTw-4RkhXE0EBJA,249
115
115
  opentrons/hardware_control/backends/estop_state.py,sha256=_GYjI6OaD3CZNduWV2_RVeOtQ4K_Fg-SP8yU01ENhCY,6554
116
- opentrons/hardware_control/backends/flex_protocol.py,sha256=PlGUMXTRISFl9c-ZxU2TdPDursLAprPw6ZLqtGMngIE,12030
117
- opentrons/hardware_control/backends/ot3controller.py,sha256=3y18NJbFDKMkfLk4X8ApIcZ9n3EMtZqCqag5hTUS74g,61020
118
- opentrons/hardware_control/backends/ot3simulator.py,sha256=N63d7Qf1DHg_-sVxsRZsKoJIZtTRJIRVNZL2V_EE5Os,29502
119
- opentrons/hardware_control/backends/ot3utils.py,sha256=Y0V26XqPxKzZmMhc0pBdfibC_En9pWmU77kyD7tDHOg,21955
116
+ opentrons/hardware_control/backends/flex_protocol.py,sha256=fn18nbNYYtyicmzT12Zb4DM-6Dqam9MPnwz8fYnAwHo,12300
117
+ opentrons/hardware_control/backends/ot3controller.py,sha256=EgNDamZwJN3pF_3q-yAI9tXXS1sdoEuv_1lggwNrCV4,62302
118
+ opentrons/hardware_control/backends/ot3simulator.py,sha256=kPXTh8OtTPB0iB56X_MIrmQ4p_fTmQMLFZLP_iqcPv4,29708
119
+ opentrons/hardware_control/backends/ot3utils.py,sha256=Olxqly0LzQXTQKTvNuWbao9wLb3Csu5cqUFtxgQfGIw,23493
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
@@ -151,13 +151,13 @@ opentrons/hardware_control/instruments/__init__.py,sha256=wS7omegQYF7BQVmd-h4U6A
151
151
  opentrons/hardware_control/instruments/instrument_abc.py,sha256=UqKb7_8E4ivlTV2tEXcnfY8AdyVaHcrRAI4LhXSFSrM,1074
152
152
  opentrons/hardware_control/instruments/ot2/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
153
153
  opentrons/hardware_control/instruments/ot2/instrument_calibration.py,sha256=llfCslA5iQp978S5KEOGH-SbFzZN7LY7PvDZnDWYUg8,4730
154
- opentrons/hardware_control/instruments/ot2/pipette.py,sha256=jE2Mvr6i03BaQG1otJSkzVqJ_PRdbS7epk-ygXjQeQQ,28518
154
+ opentrons/hardware_control/instruments/ot2/pipette.py,sha256=uUiOQX1KNP-wzSDM4vSQ6oqK1655CKSxWi7QhCPy8Z8,28706
155
155
  opentrons/hardware_control/instruments/ot2/pipette_handler.py,sha256=pu_pEdlrmStUfbJS8Yq4HuA7PfNZdbk_TEbxx3lrVIU,37673
156
156
  opentrons/hardware_control/instruments/ot3/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
157
157
  opentrons/hardware_control/instruments/ot3/gripper.py,sha256=h90Me-spIO4myXF_afgf4Fsjw_4uQsyMwwWxAD1gQ1U,13706
158
158
  opentrons/hardware_control/instruments/ot3/gripper_handler.py,sha256=GVz6QreaYSo-Vt2b-NHnMV2ehjlDxJCWaqq5ufQYCYM,6055
159
159
  opentrons/hardware_control/instruments/ot3/instrument_calibration.py,sha256=9ERAxnUHc2BBoLPloLwGoNDqssKsjzBQv1cOjwp8umk,5432
160
- opentrons/hardware_control/instruments/ot3/pipette.py,sha256=rrbN1Du1pTL8-pZ19K38ns_w5SMDMHcUG3Wh5nuxdWo,32493
160
+ opentrons/hardware_control/instruments/ot3/pipette.py,sha256=oygUZaCVRfV58NuLAK-rJa0VdNSVBYuj53JXw9WDbaw,32826
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
163
  opentrons/hardware_control/modules/absorbance_reader.py,sha256=Z6ikQiySpDi4tP1VxT-Q0WCNLUqfyzJ-iw7B7t-CUNI,13573
@@ -220,17 +220,17 @@ opentrons/protocol_api/config.py,sha256=r9lyvXjagTX_g3q5FGURPpcz2IA9sSF7Oa_1mKx-
220
220
  opentrons/protocol_api/create_protocol_context.py,sha256=wwsZje0L__oDnu1Yrihau320_f-ASloR9eL1QCtkOh8,7612
221
221
  opentrons/protocol_api/deck.py,sha256=94vFceg1SC1bAGd7TvC1ZpYwnJR-VlzurEZ6jkacYeg,8910
222
222
  opentrons/protocol_api/disposal_locations.py,sha256=NRiSGmDR0LnbyEkWSOM-o64uR2fUoB1NWJG7Y7SsJSs,7920
223
- opentrons/protocol_api/instrument_context.py,sha256=EnOs4BAmpIabaWMFKCqhwIJcrWpP-iEv32kmdV2t9Uo,97275
223
+ opentrons/protocol_api/instrument_context.py,sha256=FvzLDaHzLI9XwO9Kq-8TanfYdJN4iICwHhYPNK__zZ0,97881
224
224
  opentrons/protocol_api/labware.py,sha256=qwjAO1Au9Ujx5jyGNQ0-HLDBOXUygnXNSuv5696tGCw,48177
225
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
229
- opentrons/protocol_api/validation.py,sha256=bWRJOEw0cAqq9lRAXbhHHXGrl0hjjTiW3KW9SJa8f_A,18397
229
+ opentrons/protocol_api/validation.py,sha256=wtmjgcn04q4SMBvtPOc7gRSTSVPN5kt0261hqLPOvyU,18458
230
230
  opentrons/protocol_api/core/__init__.py,sha256=-g74o8OtBB0LmmOvwkRvPgrHt7fF7T8FRHDj-x_-Onk,736
231
231
  opentrons/protocol_api/core/common.py,sha256=sXWlP8F4ZAEGcDh1WuavrVxkoi-FultBbpY6JW0_9ZY,1029
232
232
  opentrons/protocol_api/core/core_map.py,sha256=gq3CIYPxuPvozf8yj8FprqBfs3e4ZJGQ6s0ViPbwV08,1757
233
- opentrons/protocol_api/core/instrument.py,sha256=C-noyr_D6164NwcHEU5RYLnwgAwj1u3ocF3gTGaeS0Q,9474
233
+ opentrons/protocol_api/core/instrument.py,sha256=BLJbOwQCtQcwdI5YeHf067pzny-pEjRTTFTD2Aq3EwI,9561
234
234
  opentrons/protocol_api/core/labware.py,sha256=SuHs0dP28F5pMWzeU1Ih0zzuD8h9Doyvmu2xIl14g7g,3946
235
235
  opentrons/protocol_api/core/module.py,sha256=p02IstVzfSqTpwbfiCYkPv_xpeb78Hi__PQKJPePxNs,12548
236
236
  opentrons/protocol_api/core/protocol.py,sha256=YUVZfbZvbRLOCLNZN5UKvmw8pwobiuL114GdMpmoLu4,7311
@@ -239,7 +239,7 @@ opentrons/protocol_api/core/well_grid.py,sha256=BU28DKaBgEU_JdZ6pEzrwNxmuh6TkO4z
239
239
  opentrons/protocol_api/core/engine/__init__.py,sha256=B_5T7zgkWDb1mXPg4NbT-wBkQaK-WVokMMnJRNu7xiM,582
240
240
  opentrons/protocol_api/core/engine/deck_conflict.py,sha256=0viwOidafVd0XhS8C7V72i68-ZYzHfDUNeqGozTA1G8,12012
241
241
  opentrons/protocol_api/core/engine/exceptions.py,sha256=aZgNrmYEeuPZm21nX_KZYtvyjv5h_zPjxxgPkEV7_bw,725
242
- opentrons/protocol_api/core/engine/instrument.py,sha256=BMqizINMrhZFjJmA0IXsD65xXvpA10V96y-S_JYb-NY,36075
242
+ opentrons/protocol_api/core/engine/instrument.py,sha256=5MtrHVEGDl29HiRtPNcYRnKsV3ha8dwSm8NT__CRmJw,36588
243
243
  opentrons/protocol_api/core/engine/labware.py,sha256=ra6leDHW9sEmvjPJIIx8iMVVIeFuAyJlLv3H92Ww7TM,7568
244
244
  opentrons/protocol_api/core/engine/load_labware_params.py,sha256=cwbmGyYp5ZOyANtEm6KKwT_n8fnYc5RysBst9nRh7Ls,4607
245
245
  opentrons/protocol_api/core/engine/module_core.py,sha256=TWqMqRgekBISMOEtlveDByJQI6JfSu-rvYOnqQbTgGY,26923
@@ -252,7 +252,7 @@ opentrons/protocol_api/core/engine/well.py,sha256=9rbu0YbCnIz9j7OU1T2LEIWcdCclCI
252
252
  opentrons/protocol_api/core/legacy/__init__.py,sha256=_9jCJNKG3SlS_vljVu8HHkZmtLf4F-f-JHALLF5d5go,401
253
253
  opentrons/protocol_api/core/legacy/deck.py,sha256=qHqcGo-Kdkl9L1aOE0pwrm9tsAnwkXbt4rIOr_VEP-s,13955
254
254
  opentrons/protocol_api/core/legacy/labware_offset_provider.py,sha256=uNNeHecIz_A9u19QalpVKF7pxloHqLg27EFcD9dbYtc,3735
255
- opentrons/protocol_api/core/legacy/legacy_instrument_core.py,sha256=irXO6YbGgp7LWQwia6rLqlwRWIu1SmxCj1YS3gKEzWc,21993
255
+ opentrons/protocol_api/core/legacy/legacy_instrument_core.py,sha256=UQl2VvtAhKN6bMqa_QjDgf5bFhRcSkLrXsCQhMWFWHg,22069
256
256
  opentrons/protocol_api/core/legacy/legacy_labware_core.py,sha256=0LeoH2vIFTuPzXFdSSeddIWcnjdMRTPpOAEsuvLDtBU,7901
257
257
  opentrons/protocol_api/core/legacy/legacy_module_core.py,sha256=tUhj88NKBMjCmCg6wjh1e2HX4d5hxjh8ZeJiYXaTaGY,23111
258
258
  opentrons/protocol_api/core/legacy/legacy_protocol_core.py,sha256=zoD5kqOZ4TUdZeFx2OdRLhMq9M9i7yATdqhynR6KNcM,21490
@@ -261,7 +261,7 @@ opentrons/protocol_api/core/legacy/load_info.py,sha256=j-fzoUKwvXNS_5CQsE43XI5YO
261
261
  opentrons/protocol_api/core/legacy/module_geometry.py,sha256=wbWeHomppdCDmp_nKrnNWbnRAapOamkhFMnaoY7ShTw,20886
262
262
  opentrons/protocol_api/core/legacy/well_geometry.py,sha256=dCsDIpMbaIpb0AW_VrnSIJdRYi8ombyMpaA7n1X46Jg,4643
263
263
  opentrons/protocol_api/core/legacy_simulator/__init__.py,sha256=m9bLHGDJ6LSYC2WPm8tpOuu0zWSOPIrlybQgjRQBw9k,647
264
- opentrons/protocol_api/core/legacy_simulator/legacy_instrument_core.py,sha256=mcHgeVT1lXscc4wvCFAcOrfB7lq4JaAVOWo8aAc4_xQ,18655
264
+ opentrons/protocol_api/core/legacy_simulator/legacy_instrument_core.py,sha256=f5eGPuNwihDoYt-EOCWfAtHLqf3Zqsxu_iawuiSxVJo,18731
265
265
  opentrons/protocol_api/core/legacy_simulator/legacy_protocol_core.py,sha256=28HrrHzeUfnGKXpZqQ-VM8WbPiadqVhKj2S9y33q6Lo,2910
266
266
  opentrons/protocol_engine/__init__.py,sha256=35cWIWtg_0OO-9v6sgWNuiJ5siDM9y77K3KsuCY69K4,3253
267
267
  opentrons/protocol_engine/create_protocol_engine.py,sha256=tfDIsC7_JKlRiCXPB_8tuxRsssU6o0ViRmWbGPtX9QA,7582
@@ -298,7 +298,7 @@ opentrons/protocol_engine/commands/generate_command_schema.py,sha256=w5RsTJV4HtF
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
300
300
  opentrons/protocol_engine/commands/home.py,sha256=pmiul9vLcpVijHAOBwzkdHulW5tBCEiEcNey3bCXdAE,3075
301
- opentrons/protocol_engine/commands/liquid_probe.py,sha256=nmrO2Mu65O1MwDnaeK-PcAMi1AKw4AtNFL6urydVN4o,12068
301
+ opentrons/protocol_engine/commands/liquid_probe.py,sha256=HjHdUtqj_A81KYA8zCU1uDyNLdpu0ntAcU4pnYINX-4,12332
302
302
  opentrons/protocol_engine/commands/load_labware.py,sha256=UQSGj0dbfnkGjYxwPrQkVm2CNyrJYemu8L7g2xDhlt0,7584
303
303
  opentrons/protocol_engine/commands/load_liquid.py,sha256=MIwzfqhzKR71DEALKvgcnykp1CbJ3kkssClLrT7jPrY,2781
304
304
  opentrons/protocol_engine/commands/load_module.py,sha256=Vgu_O64tfDNgqAhVlVcjVrejt1TeKr_8ziar7L17-yw,7499
@@ -325,7 +325,7 @@ opentrons/protocol_engine/commands/absorbance_reader/__init__.py,sha256=umS98Llk
325
325
  opentrons/protocol_engine/commands/absorbance_reader/close_lid.py,sha256=i6kggql8hJgTxpUbc5dmdMdHDsYWiczfMvZNA72FmvI,5540
326
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
- opentrons/protocol_engine/commands/absorbance_reader/read.py,sha256=xu5KijW_sSEtjdhNIEctYkQBZC8Baj650W6s8nXfTKw,8187
328
+ opentrons/protocol_engine/commands/absorbance_reader/read.py,sha256=QZ8zjjlKQPXprekxsEjgFxmvLns4Q0NpnZ6hbFAoq6k,8403
329
329
  opentrons/protocol_engine/commands/calibration/__init__.py,sha256=JjNnULLBM3j8VtpfHOvH51em9jVLR_ezyrUJUWqxuYI,1611
330
330
  opentrons/protocol_engine/commands/calibration/calibrate_gripper.py,sha256=1HgEsH-0ycGD-OlxdmCTIuBeSCzVTc1vepSjVirai-g,5570
331
331
  opentrons/protocol_engine/commands/calibration/calibrate_module.py,sha256=zjkgPgHHn5CM-ztvbcKcTaAMvj9zsMLNyykynwec0j0,4172
@@ -363,7 +363,7 @@ opentrons/protocol_engine/commands/unsafe/unsafe_blow_out_in_place.py,sha256=K1i
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
365
  opentrons/protocol_engine/commands/unsafe/unsafe_place_labware.py,sha256=etNdLxj0TtmBA5fTLR-OSfbnRNbXKDzE__aKRAkvWMM,7866
366
- opentrons/protocol_engine/commands/unsafe/unsafe_ungrip_labware.py,sha256=MSYftVeLHqnIyX0n9IJLq6XOb7P835EQg7KR3n3PU6c,2397
366
+ opentrons/protocol_engine/commands/unsafe/unsafe_ungrip_labware.py,sha256=408KbcJgroOA7Qhs-qih-ce-2n-ixGKu6mhv33TplJ0,2454
367
367
  opentrons/protocol_engine/commands/unsafe/update_position_estimators.py,sha256=Jgds_cwEHBiiDyJVK6wL34VTWZikrSVYJSRUohnsod8,3044
368
368
  opentrons/protocol_engine/errors/__init__.py,sha256=4CEG_WmrdSmESVkSkwfLlbNwW_r8TlMbD_fEXzcn5qg,5232
369
369
  opentrons/protocol_engine/errors/error_occurrence.py,sha256=Uoa0tqJ4C-oosDUwk-XXf0NxgWVnVnnhTFOqJAT1J_o,7896
@@ -399,7 +399,7 @@ opentrons/protocol_engine/resources/labware_validation.py,sha256=3Rc--uqHJ6dSoT8
399
399
  opentrons/protocol_engine/resources/model_utils.py,sha256=C3OHUi-OtuFUm3dS5rApSU3EJ0clnaCZEyBku5sTjzA,941
400
400
  opentrons/protocol_engine/resources/module_data_provider.py,sha256=fU4l1Wkeb1odW6XekvC0_SS0KjzAOcHPJQ4dLMp74NU,1553
401
401
  opentrons/protocol_engine/resources/ot3_validation.py,sha256=0x81JoZBXcj2xUVcOF7v5ETc8y5T_sbs-jTPxuSnooE,744
402
- opentrons/protocol_engine/resources/pipette_data_provider.py,sha256=EVPvsMDyTduyQvkULF_c58qirH251bPusbNvZr8YCjA,14788
402
+ opentrons/protocol_engine/resources/pipette_data_provider.py,sha256=FRKp7lRl6jaOUK-aQBcf2W8rjjAwsJjIcqe-AWClr-8,15416
403
403
  opentrons/protocol_engine/state/__init__.py,sha256=hDdA4GjXbi9h7K_FMbQGT9tOw3YtRNn5LIryMdkotS8,36
404
404
  opentrons/protocol_engine/state/_abstract_store.py,sha256=b5cqKZhI6ERZj6gyL0kDutD6ogdQngR3T-JmPATvhi8,631
405
405
  opentrons/protocol_engine/state/_move_types.py,sha256=zSQj_qYHBi7_-wrpaZBKmX_O-wNZCpLZkCzagOwI-zY,2132
@@ -412,9 +412,9 @@ opentrons/protocol_engine/state/frustum_helpers.py,sha256=OMa7_3INieaNhkFb6wVC6l
412
412
  opentrons/protocol_engine/state/geometry.py,sha256=-yEsGHPfjUVNgVWJKeBiJyzl65wAzSvjS7W-kOxukCQ,67902
413
413
  opentrons/protocol_engine/state/labware.py,sha256=Ns_GoUKxL55P6J2YEch5GZCVvTbQZS0h4CbZwCr7Frc,44758
414
414
  opentrons/protocol_engine/state/liquids.py,sha256=TwchzB7xNt7CAd3wX3BA_5DXrQhGwj_uFQsv0FWOXXk,1886
415
- opentrons/protocol_engine/state/modules.py,sha256=8d5YOPhGs8jnw5mDQiGSrhtvozZkCtcRQd49YesVmds,53936
415
+ opentrons/protocol_engine/state/modules.py,sha256=NCx6s9WhFnv-XXIcnfnJ_rqLelI7pzqIv8v5ACrG2xE,54110
416
416
  opentrons/protocol_engine/state/motion.py,sha256=1KEm1HXdkuFKNe2lElZnNfJedml4afMFnmcDvG-3fLA,14937
417
- opentrons/protocol_engine/state/pipettes.py,sha256=jXhPGLHT1Ypc_tt8UkWZz51sCZ9ld4umr7AyuhlvxoU,30861
417
+ opentrons/protocol_engine/state/pipettes.py,sha256=2PTtV1vkadi8m3XzjXomnhKSs_e6tfsxWaoAkYHPkNw,31260
418
418
  opentrons/protocol_engine/state/state.py,sha256=HbkGfTLYNPlb9yu2sCi4UOJQrgXedi3w8xbYc0QCn-w,14550
419
419
  opentrons/protocol_engine/state/state_summary.py,sha256=vTA4MLVtObroLSVEQFKspwE9hkKLfNHKacUA5QVIF5c,1058
420
420
  opentrons/protocol_engine/state/tips.py,sha256=_1LZncAdhCavaKs2NBFKQAmTyExIorQw7vHOqomeb8g,20993
@@ -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.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,,
515
+ opentrons-8.3.0a0.dist-info/LICENSE,sha256=z8d0m5b2O9McPEK1xHG_dWgUBT6EfBDz6wA0F7xSPTA,11358
516
+ opentrons-8.3.0a0.dist-info/METADATA,sha256=9iQE3Sd4b1FTISPSJqyqy7PcntPkVpftdiMDRPi8ZdI,5019
517
+ opentrons-8.3.0a0.dist-info/WHEEL,sha256=_4XEmVmaBFWtekSGrbfOGNjC2I5lUr0lZSRblBllIFA,109
518
+ opentrons-8.3.0a0.dist-info/entry_points.txt,sha256=fTa6eGCYkvOtv0ov-KVE8LLGetgb35LQLF9x85OWPVw,106
519
+ opentrons-8.3.0a0.dist-info/top_level.txt,sha256=wk6whpbMZdBQpcK0Fg0YVfUGrAgVOFON7oQAhOMGMW8,10
520
+ opentrons-8.3.0a0.dist-info/RECORD,,