opentrons 8.4.0a7__py2.py3-none-any.whl → 8.4.0a8__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.

@@ -834,7 +834,7 @@ class InstrumentCore(AbstractInstrument[WellCore, LabwareCore]):
834
834
  )
835
835
  )
836
836
 
837
- def resin_tip_unseal(self, location: Location, well_core: WellCore) -> None:
837
+ def resin_tip_unseal(self, location: Location | None, well_core: WellCore) -> None:
838
838
  well_name = well_core.get_name()
839
839
  labware_id = well_core.labware_id
840
840
 
@@ -1188,9 +1188,9 @@ class InstrumentCore(AbstractInstrument[WellCore, LabwareCore]):
1188
1188
  cmd.GetNextTipParams(
1189
1189
  pipetteId=self._pipette_id,
1190
1190
  labwareIds=[tip_rack.labware_id for tip_rack in valid_tip_racks],
1191
- startingTipWell=starting_well.get_name()
1192
- if starting_well is not None
1193
- else None,
1191
+ startingTipWell=(
1192
+ starting_well.get_name() if starting_well is not None else None
1193
+ ),
1194
1194
  )
1195
1195
  )
1196
1196
  next_tip_info = result.nextTipInfo
@@ -206,7 +206,7 @@ class AbstractInstrument(ABC, Generic[WellCoreType, LabwareCoreType]):
206
206
  @abstractmethod
207
207
  def resin_tip_unseal(
208
208
  self,
209
- location: types.Location,
209
+ location: types.Location | None,
210
210
  well_core: WellCoreType,
211
211
  ) -> None:
212
212
  ...
@@ -332,7 +332,7 @@ class LegacyInstrumentCore(AbstractInstrument[LegacyWellCore, LegacyLabwareCore]
332
332
 
333
333
  def resin_tip_unseal(
334
334
  self,
335
- location: types.Location,
335
+ location: types.Location | None,
336
336
  well_core: WellCore,
337
337
  ) -> None:
338
338
  raise APIVersionError(api_element="Unsealing resin tips.")
@@ -298,7 +298,7 @@ class LegacyInstrumentCoreSimulator(
298
298
 
299
299
  def resin_tip_unseal(
300
300
  self,
301
- location: types.Location,
301
+ location: types.Location | None,
302
302
  well_core: WellCore,
303
303
  ) -> None:
304
304
  raise APIVersionError(api_element="Unsealing resin tips.")
@@ -606,7 +606,20 @@ class InstrumentContext(publisher.CommandPublisher):
606
606
  "Blow_out being performed on a tiprack. "
607
607
  "Please re-check your code"
608
608
  )
609
- move_to_location = target.location or target.well.top()
609
+ if target.location:
610
+ # because the lower levels of blowout don't handle LiquidHandlingWellLocation and
611
+ # there is no "operation_volume" for blowout we need to convert the relative location
612
+ # given with a .meniscus to an absolute point. To maintain the meniscus behavior
613
+ # we can just add the offset to the current liquid height.
614
+ if target.location.meniscus_tracking:
615
+ move_to_location = target.well.bottom(
616
+ target.well.current_liquid_height() # type: ignore [arg-type]
617
+ + target.location.point.z
618
+ )
619
+ else:
620
+ move_to_location = target.location
621
+ else:
622
+ move_to_location = target.well.top()
610
623
  well = target.well
611
624
  elif isinstance(target, validation.PointTarget):
612
625
  move_to_location = target.location
@@ -1611,9 +1624,9 @@ class InstrumentContext(publisher.CommandPublisher):
1611
1624
  (types.Location(types.Point(), labware=rack), rack._core)
1612
1625
  for rack in transfer_args.tip_racks
1613
1626
  ],
1614
- starting_tip=self.starting_tip._core
1615
- if self.starting_tip is not None
1616
- else None,
1627
+ starting_tip=(
1628
+ self.starting_tip._core if self.starting_tip is not None else None
1629
+ ),
1617
1630
  trash_location=transfer_args.trash_location,
1618
1631
  return_tip=return_tip,
1619
1632
  )
@@ -1727,9 +1740,9 @@ class InstrumentContext(publisher.CommandPublisher):
1727
1740
  (types.Location(types.Point(), labware=rack), rack._core)
1728
1741
  for rack in transfer_args.tip_racks
1729
1742
  ],
1730
- starting_tip=self.starting_tip._core
1731
- if self.starting_tip is not None
1732
- else None,
1743
+ starting_tip=(
1744
+ self.starting_tip._core if self.starting_tip is not None else None
1745
+ ),
1733
1746
  trash_location=transfer_args.trash_location,
1734
1747
  return_tip=return_tip,
1735
1748
  )
@@ -1843,9 +1856,9 @@ class InstrumentContext(publisher.CommandPublisher):
1843
1856
  (types.Location(types.Point(), labware=rack), rack._core)
1844
1857
  for rack in transfer_args.tip_racks
1845
1858
  ],
1846
- starting_tip=self.starting_tip._core
1847
- if self.starting_tip is not None
1848
- else None,
1859
+ starting_tip=(
1860
+ self.starting_tip._core if self.starting_tip is not None else None
1861
+ ),
1849
1862
  trash_location=transfer_args.trash_location,
1850
1863
  return_tip=return_tip,
1851
1864
  )
@@ -2019,7 +2032,7 @@ class InstrumentContext(publisher.CommandPublisher):
2019
2032
  location=well,
2020
2033
  ),
2021
2034
  ):
2022
- self._core.resin_tip_unseal(location=well.top(), well_core=well._core)
2035
+ self._core.resin_tip_unseal(location=None, well_core=well._core)
2023
2036
 
2024
2037
  return self
2025
2038
 
@@ -188,6 +188,12 @@ async def _execute_common( # noqa: C901
188
188
  well_name=well_name,
189
189
  well_location=params.wellLocation,
190
190
  )
191
+ state_view.geometry.validate_probed_height(
192
+ labware_id=labware_id,
193
+ well_name=well_name,
194
+ pipette_id=pipette_id,
195
+ probed_height=z_pos,
196
+ )
191
197
  except PipetteLiquidNotFoundError as exception:
192
198
  move_result.state_update.set_pipette_ready_to_aspirate(
193
199
  pipette_id=pipette_id, ready_to_aspirate=True
@@ -1,12 +1,15 @@
1
1
  """Seal tips to pipette command request, result, and implementation models."""
2
2
 
3
3
  from __future__ import annotations
4
- from pydantic import Field, BaseModel
5
4
  from typing import TYPE_CHECKING, Optional, Type, Union
6
- from opentrons.types import MountType
7
- from opentrons.protocol_engine.types import MotorAxis
5
+
8
6
  from typing_extensions import Literal
7
+ from pydantic import Field, BaseModel
8
+
9
+ from opentrons_shared_data.errors.exceptions import PositionUnknownError
9
10
 
11
+ from opentrons.types import MountType
12
+ from opentrons.protocol_engine.types import MotorAxis
10
13
  from ..resources import ModelUtils, ensure_ot3_hardware
11
14
  from ..types import PickUpTipWellLocation, FluidKind, AspiratedFluid
12
15
  from .pipetting_common import (
@@ -27,7 +30,6 @@ from .command import (
27
30
 
28
31
  from opentrons.hardware_control import HardwareControlAPI
29
32
  from opentrons.hardware_control.types import Axis
30
- from ..state.update_types import StateUpdate
31
33
 
32
34
  if TYPE_CHECKING:
33
35
  from ..state.state import StateView
@@ -138,28 +140,50 @@ class SealPipetteToTipImplementation(
138
140
  """A relative press-fit pick up command using gantry moves."""
139
141
  prep_distance = tip_pick_up_params.prepDistance
140
142
  press_distance = tip_pick_up_params.pressDistance
141
- retract_distance = -1 * (prep_distance + press_distance)
143
+ retract_distance = -1 * (press_distance) / 2
142
144
 
143
145
  mount_axis = MotorAxis.LEFT_Z if mount == MountType.LEFT else MotorAxis.RIGHT_Z
144
-
146
+ ot3_hardware_api = ensure_ot3_hardware(self._hardware_api)
145
147
  # TODO chb, 2025-01-29): Factor out the movement constants and relocate this logic into the hardware controller
146
- await self._gantry_mover.move_axes(
147
- axis_map={mount_axis: prep_distance}, speed=10, relative_move=True
148
+ try:
149
+ await self._gantry_mover.move_axes(
150
+ axis_map={mount_axis: prep_distance},
151
+ speed=10,
152
+ relative_move=True,
153
+ expect_stalls=True,
154
+ )
155
+ except PositionUnknownError:
156
+ # if this happens it's from the get position after the move and we can ignore it
157
+ pass
158
+
159
+ await ot3_hardware_api.update_axis_position_estimations(
160
+ self._gantry_mover.motor_axes_to_present_hardware_axes([mount_axis])
148
161
  )
149
162
 
150
163
  # Drive mount down for press-fit
151
- await self._gantry_mover.move_axes(
152
- axis_map={mount_axis: press_distance},
153
- speed=10.0,
154
- relative_move=True,
155
- expect_stalls=True,
156
- )
157
- # retract cam : 11.05
158
- await self._gantry_mover.move_axes(
159
- axis_map={mount_axis: retract_distance}, speed=5.5, relative_move=True
164
+ try:
165
+ await self._gantry_mover.move_axes(
166
+ axis_map={mount_axis: press_distance},
167
+ speed=10.0,
168
+ relative_move=True,
169
+ expect_stalls=True,
170
+ )
171
+ except PositionUnknownError:
172
+ # if this happens it's from the get position after the move and we can ignore it
173
+ pass
174
+
175
+ await ot3_hardware_api.update_axis_position_estimations(
176
+ self._gantry_mover.motor_axes_to_present_hardware_axes([mount_axis])
160
177
  )
161
178
 
162
- ot3_hardware_api = ensure_ot3_hardware(self._hardware_api)
179
+ try:
180
+ await self._gantry_mover.move_axes(
181
+ axis_map={mount_axis: retract_distance}, speed=5.5, relative_move=True
182
+ )
183
+ except PositionUnknownError:
184
+ # if this happens it's from the get position after the move and we can ignore it
185
+ pass
186
+
163
187
  await ot3_hardware_api.update_axis_position_estimations(
164
188
  self._gantry_mover.motor_axes_to_present_hardware_axes([mount_axis])
165
189
  )
@@ -283,13 +307,10 @@ class SealPipetteToTipImplementation(
283
307
  if hw_instr is not None:
284
308
  hw_instr.set_current_volume(_SAFE_TOP_VOLUME)
285
309
 
286
- state_update = StateUpdate()
287
- state_update.update_pipette_tip_state(
310
+ state_update = move_result.state_update.update_pipette_tip_state(
288
311
  pipette_id=pipette_id,
289
312
  tip_geometry=tip_geometry,
290
- )
291
-
292
- state_update.set_fluid_aspirated(
313
+ ).set_fluid_aspirated(
293
314
  pipette_id=pipette_id,
294
315
  fluid=AspiratedFluid(kind=FluidKind.LIQUID, volume=_SAFE_TOP_VOLUME),
295
316
  )
@@ -3,12 +3,10 @@
3
3
  from __future__ import annotations
4
4
 
5
5
  from pydantic import Field
6
- from typing import TYPE_CHECKING, Optional, Type
6
+ from typing import TYPE_CHECKING, Optional, Type, Final
7
7
  from typing_extensions import Literal
8
8
 
9
9
  from opentrons.protocol_engine.resources.model_utils import ModelUtils
10
- from opentrons.protocol_engine.types import MotorAxis
11
- from opentrons.types import MountType
12
10
 
13
11
  from ..types import DropTipWellLocation
14
12
  from .pipetting_common import (
@@ -56,6 +54,8 @@ _ExecuteReturn = (
56
54
  SuccessData[UnsealPipetteFromTipResult] | DefinedErrorData[StallOrCollisionError]
57
55
  )
58
56
 
57
+ CUSTOM_TIP_LENGTH_MARGIN: Final = 10
58
+
59
59
 
60
60
  class UnsealPipetteFromTipImplementation(
61
61
  AbstractCommandImpl[UnsealPipetteFromTipParams, _ExecuteReturn]
@@ -85,6 +85,10 @@ class UnsealPipetteFromTipImplementation(
85
85
 
86
86
  well_location = params.wellLocation
87
87
 
88
+ tip_geometry = self._state_view.geometry.get_nominal_tip_geometry(
89
+ pipette_id, labware_id, well_name
90
+ )
91
+
88
92
  is_partially_configured = self._state_view.pipettes.get_is_partially_configured(
89
93
  pipette_id=pipette_id
90
94
  )
@@ -93,6 +97,7 @@ class UnsealPipetteFromTipImplementation(
93
97
  labware_id=labware_id,
94
98
  well_location=well_location,
95
99
  partially_configured=is_partially_configured,
100
+ override_default_offset=-(tip_geometry.length - CUSTOM_TIP_LENGTH_MARGIN),
96
101
  )
97
102
 
98
103
  move_result = await move_to_well(
@@ -106,14 +111,6 @@ class UnsealPipetteFromTipImplementation(
106
111
  if isinstance(move_result, DefinedErrorData):
107
112
  return move_result
108
113
 
109
- # Move to an appropriate position
110
- mount = self._state_view.pipettes.get_mount(pipette_id)
111
-
112
- mount_axis = MotorAxis.LEFT_Z if mount == MountType.LEFT else MotorAxis.RIGHT_Z
113
- await self._gantry_mover.move_axes(
114
- axis_map={mount_axis: -14}, speed=10, relative_move=True
115
- )
116
-
117
114
  await self._tip_handler.drop_tip(
118
115
  pipette_id=pipette_id,
119
116
  home_after=None,
@@ -88,6 +88,7 @@ from .exceptions import (
88
88
  OffsetLocationInvalidError,
89
89
  FlexStackerLabwarePoolNotYetDefinedError,
90
90
  FlexStackerNotLogicallyEmptyError,
91
+ InvalidLabwarePositionError,
91
92
  )
92
93
 
93
94
  from .error_occurrence import ErrorOccurrence, ProtocolCommandFailedError
@@ -171,6 +172,7 @@ __all__ = [
171
172
  "OffsetLocationInvalidError",
172
173
  "FlexStackerLabwarePoolNotYetDefinedError",
173
174
  "FlexStackerNotLogicallyEmptyError",
175
+ "InvalidLabwarePositionError",
174
176
  # error occurrence models
175
177
  "ErrorOccurrence",
176
178
  "CommandNotAllowedError",
@@ -1281,3 +1281,15 @@ class FlexStackerLabwarePoolNotYetDefinedError(ProtocolEngineError):
1281
1281
  wrapping: Optional[Sequence[EnumeratedError]] = None,
1282
1282
  ) -> None:
1283
1283
  super().__init__(ErrorCodes.GENERAL_ERROR, message, details, wrapping)
1284
+
1285
+
1286
+ class InvalidLabwarePositionError(ProtocolEngineError):
1287
+ """Raised when a labware position is internally invalid."""
1288
+
1289
+ def __init__(
1290
+ self,
1291
+ message: Optional[str] = None,
1292
+ details: Optional[dict[str, Any]] = None,
1293
+ wrapping: Optional[Sequence[EnumeratedError]] = None,
1294
+ ) -> None:
1295
+ super().__init__(ErrorCodes.GENERAL_ERROR, message, details, wrapping)
@@ -326,7 +326,7 @@ class HardwarePipettingHandler(PipettingHandler):
326
326
  well_name: str,
327
327
  well_location: WellLocation,
328
328
  ) -> LiquidTrackingType:
329
- """Detect liquid level."""
329
+ """Return liquid level relative to the bottom of the well."""
330
330
  hw_pipette = self._state_view.pipettes.get_hardware_pipette(
331
331
  pipette_id=pipette_id,
332
332
  attached_pipettes=self._hardware_api.attached_instruments,
@@ -17,7 +17,10 @@ from opentrons.types import (
17
17
  MeniscusTrackingTarget,
18
18
  )
19
19
 
20
- from opentrons_shared_data.errors.exceptions import InvalidStoredData
20
+ from opentrons_shared_data.errors.exceptions import (
21
+ InvalidStoredData,
22
+ PipetteLiquidNotFoundError,
23
+ )
21
24
  from opentrons_shared_data.labware.constants import WELL_NAME_PATTERN
22
25
  from opentrons_shared_data.labware.labware_definition import LabwareDefinition
23
26
  from opentrons_shared_data.deck.types import CutoutFixture
@@ -31,6 +34,8 @@ from ..errors import (
31
34
  LabwareNotLoadedOnModuleError,
32
35
  LabwareMovementNotAllowedError,
33
36
  OperationLocationNotInWellError,
37
+ InvalidLabwarePositionError,
38
+ LabwareNotOnDeckError,
34
39
  )
35
40
  from ..errors.exceptions import InvalidLiquidHeightFound
36
41
  from ..resources import (
@@ -373,6 +378,59 @@ class GeometryView:
373
378
  "Either it has been loaded off-deck or its been moved off-deck."
374
379
  )
375
380
 
381
+ def _get_offset_from_parent_addressable_area(
382
+ self, child_definition: LabwareDefinition, parent: LabwareLocation
383
+ ) -> LabwareOffsetVector:
384
+ """Gets the offset vector of a labware from its eventual parent addressable area.
385
+
386
+ This returns the sum of the offsets for any labware-on-labware pairs plus the
387
+ "base offset", which is (0, 0, 0) in all cases except for modules on the
388
+ OT-2. See
389
+ protocol_engine.state.modules.get_nominal_offset_to_child_from_addressable_area
390
+ for more.
391
+
392
+ This does not incorporate LPC offsets or module calibration offsets.
393
+ """
394
+ if isinstance(parent, (AddressableAreaLocation, DeckSlotLocation)):
395
+ return LabwareOffsetVector(x=0, y=0, z=0)
396
+ elif isinstance(parent, ModuleLocation):
397
+ module_id = parent.moduleId
398
+ module_model = self._modules.get_connected_model(module_id)
399
+ stacking_overlap = self._labware.get_module_overlap_offsets(
400
+ child_definition, module_model
401
+ )
402
+ module_to_child = (
403
+ self._modules.get_nominal_offset_to_child_from_addressable_area(
404
+ module_id=module_id
405
+ )
406
+ )
407
+ return LabwareOffsetVector(
408
+ x=module_to_child.x - stacking_overlap.x,
409
+ y=module_to_child.y - stacking_overlap.y,
410
+ z=module_to_child.z - stacking_overlap.z,
411
+ )
412
+ elif isinstance(parent, OnLabwareLocation):
413
+ on_labware = self._labware.get(parent.labwareId)
414
+ on_labware_dimensions = self._labware.get_dimensions(
415
+ labware_id=on_labware.id
416
+ )
417
+ stacking_overlap = self._labware.get_labware_overlap_offsets(
418
+ definition=child_definition, below_labware_name=on_labware.loadName
419
+ )
420
+ labware_offset = LabwareOffsetVector(
421
+ x=stacking_overlap.x,
422
+ y=stacking_overlap.y,
423
+ z=on_labware_dimensions.z - stacking_overlap.z,
424
+ )
425
+ return labware_offset + self._get_offset_from_parent_addressable_area(
426
+ self._labware.get_definition(on_labware.id), on_labware.location
427
+ )
428
+ else:
429
+ raise errors.LabwareNotOnDeckError(
430
+ "Cannot access labware since it is not on the deck. "
431
+ "Either it has been loaded off-deck or it has been moved off-deck."
432
+ )
433
+
376
434
  def _normalize_module_calibration_offset(
377
435
  self,
378
436
  module_location: DeckSlotLocation,
@@ -498,6 +556,30 @@ class GeometryView:
498
556
  f"Specifying {well_location.origin} with an offset of {well_location.offset} results in an operation location below the bottom of the well"
499
557
  )
500
558
 
559
+ def validate_probed_height(
560
+ self,
561
+ labware_id: str,
562
+ well_name: str,
563
+ pipette_id: str,
564
+ probed_height: LiquidTrackingType,
565
+ ) -> None:
566
+ """Raise an error if a probed liquid height is not within well bounds."""
567
+ if isinstance(probed_height, SimulatedProbeResult):
568
+ return
569
+ lld_min_height = self._pipettes.get_current_tip_lld_settings(
570
+ pipette_id=pipette_id
571
+ )
572
+ well_def = self._labware.get_well_definition(labware_id, well_name)
573
+ well_depth = well_def.depth
574
+ if probed_height < lld_min_height:
575
+ raise PipetteLiquidNotFoundError(
576
+ f"Liquid Height of {probed_height} mm is lower minumum allowed lld height {lld_min_height} mm."
577
+ )
578
+ if probed_height > well_depth:
579
+ raise PipetteLiquidNotFoundError(
580
+ f"Liquid Height of {probed_height} mm is greater than maximum well height {well_depth} mm."
581
+ )
582
+
501
583
  def get_well_position(
502
584
  self,
503
585
  labware_id: str,
@@ -695,6 +777,7 @@ class GeometryView:
695
777
  labware_id: str,
696
778
  well_location: DropTipWellLocation,
697
779
  partially_configured: bool = False,
780
+ override_default_offset: float | None = None,
698
781
  ) -> WellLocation:
699
782
  """Get tip drop location given labware and hardware pipette.
700
783
 
@@ -713,8 +796,9 @@ class GeometryView:
713
796
  origin=WellOrigin(well_location.origin.value),
714
797
  offset=well_location.offset,
715
798
  )
716
-
717
- if self._labware.get_definition(labware_id).parameters.isTiprack:
799
+ if override_default_offset is not None:
800
+ z_offset = override_default_offset
801
+ elif self._labware.get_definition(labware_id).parameters.isTiprack:
718
802
  z_offset = self._labware.get_tip_drop_z_offset(
719
803
  labware_id=labware_id,
720
804
  length_scale=self._pipettes.get_return_tip_scale(pipette_id),
@@ -795,6 +879,32 @@ class GeometryView:
795
879
 
796
880
  return slot_name
797
881
 
882
+ def get_ancestor_addressable_area_name(self, labware_id: str) -> str:
883
+ """Get the name of the addressable area the labware is eventually on."""
884
+ labware = self._labware.get(labware_id)
885
+ original_display_name = self._labware.get_display_name(labware_id)
886
+ seen: Set[str] = set((labware_id,))
887
+ while isinstance(labware.location, OnLabwareLocation):
888
+ labware = self._labware.get(labware.location.labwareId)
889
+ if labware.id in seen:
890
+ raise InvalidLabwarePositionError(
891
+ f"Cycle detected in labware positioning for {original_display_name}"
892
+ )
893
+ seen.add(labware.id)
894
+ if isinstance(labware.location, DeckSlotLocation):
895
+ return labware.location.slotName.id
896
+ elif isinstance(labware.location, AddressableAreaLocation):
897
+ return labware.location.addressableAreaName
898
+ elif isinstance(labware.location, ModuleLocation):
899
+ return self._modules.get_provided_addressable_area(
900
+ labware.location.moduleId
901
+ )
902
+ else:
903
+ raise LabwareNotOnDeckError(
904
+ f"Labware {original_display_name} is not loaded on deck",
905
+ details={"eventual-location": repr(labware.location)},
906
+ )
907
+
798
908
  def ensure_location_not_occupied(
799
909
  self,
800
910
  location: _LabwareLocation,
@@ -962,70 +1072,23 @@ class GeometryView:
962
1072
  self._labware.get_grip_height_from_labware_bottom(labware_definition)
963
1073
  )
964
1074
  location_name: str
965
- module_location: ModuleLocation | None = None
966
-
1075
+ offset = self._get_offset_from_parent_addressable_area(
1076
+ child_definition=labware_definition, parent=location
1077
+ ) + self._get_calibrated_module_offset(location)
967
1078
  if isinstance(location, DeckSlotLocation):
968
1079
  location_name = location.slotName.id
969
- offset = LabwareOffsetVector(x=0, y=0, z=0)
970
1080
  elif isinstance(location, AddressableAreaLocation):
971
1081
  location_name = location.addressableAreaName
972
- if fixture_validation.is_gripper_waste_chute(location_name):
973
- drop_labware_location = (
974
- self._addressable_areas.get_addressable_area_move_to_location(
975
- location_name
976
- )
977
- )
978
- return drop_labware_location + Point(z=grip_height_from_labware_bottom)
979
- # Location should have been pre-validated so this will be a deck/staging area slot
980
- else:
981
- offset = LabwareOffsetVector(x=0, y=0, z=0)
982
- else:
983
- if isinstance(location, ModuleLocation):
984
- location_name = self._modules.get_provided_addressable_area(
985
- location.moduleId
986
- )
987
- module_location = location
988
- else: # OnLabwareLocation
989
- labware_loc = self._labware.get(location.labwareId).location
990
- if isinstance(labware_loc, ModuleLocation):
991
- location_name = self._modules.get_provided_addressable_area(
992
- labware_loc.moduleId
993
- )
994
- module_location = labware_loc
995
- else:
996
- location_name = self.get_ancestor_slot_name(location.labwareId).id
997
- labware_offset = self._get_offset_from_parent(
998
- child_definition=labware_definition, parent=location
999
- )
1000
- # Get the calibrated offset if the on labware location is on top of a module, otherwise return empty one
1001
- cal_offset = self._get_calibrated_module_offset(location)
1002
- offset = LabwareOffsetVector(
1003
- x=labware_offset.x + cal_offset.x,
1004
- y=labware_offset.y + cal_offset.y,
1005
- z=labware_offset.z + cal_offset.z,
1006
- )
1007
-
1008
- if module_location is not None:
1009
- # Location center must be determined from the cutout the Module is loaded in
1010
- position = deck_configuration_provider.get_cutout_position(
1011
- cutout_id=self._addressable_areas.get_cutout_id_by_deck_slot_name(
1012
- self._modules.get_location(module_location.moduleId).slotName
1013
- ),
1014
- deck_definition=self._addressable_areas.deck_definition,
1015
- )
1016
- bounding_box = self._addressable_areas.get_addressable_area(
1017
- location_name
1018
- ).bounding_box
1019
- location_center = Point(
1020
- position.x + bounding_box.x / 2,
1021
- position.y + bounding_box.y / 2,
1022
- position.z,
1082
+ elif isinstance(location, ModuleLocation):
1083
+ location_name = self._modules.get_provided_addressable_area(
1084
+ location.moduleId
1023
1085
  )
1086
+ else: # OnLabwareLocation
1087
+ location_name = self.get_ancestor_addressable_area_name(location.labwareId)
1024
1088
 
1025
- else:
1026
- location_center = self._addressable_areas.get_addressable_area_center(
1027
- location_name
1028
- )
1089
+ location_center = self._addressable_areas.get_addressable_area_center(
1090
+ location_name
1091
+ )
1029
1092
 
1030
1093
  return Point(
1031
1094
  location_center.x + offset.x,
@@ -1464,10 +1527,6 @@ class GeometryView:
1464
1527
  # * The "additional offset" or "user offset", e.g. the `pickUpOffset` and `dropOffset`
1465
1528
  # params in the `moveLabware` command.
1466
1529
  #
1467
- # And this *does* take these extra offsets into account:
1468
- #
1469
- # * The labware's Labware Position Check offset
1470
- #
1471
1530
  # For robustness, we should combine this with `get_gripper_labware_movement_waypoints()`.
1472
1531
  #
1473
1532
  # We should also be more explicit about which offsets act to move the gripper paddles
@@ -1489,23 +1548,17 @@ class GeometryView:
1489
1548
  return
1490
1549
 
1491
1550
  tip = self._pipettes.get_attached_tip(pipette.id)
1492
- if tip:
1493
- # NOTE: This call to get_labware_highest_z() uses the labware's LPC offset,
1494
- # which is an inconsistency between this and the actual gripper movement.
1495
- # See the todo comment above this function.
1496
- labware_top_z_when_gripped = gripper_homed_position_z + (
1497
- self.get_labware_highest_z(labware_id=labware_id)
1498
- - self.get_labware_grip_point(
1499
- labware_definition=labware_definition, location=current_location
1500
- ).z
1551
+ if not tip:
1552
+ continue
1553
+ labware_top_z_when_gripped = gripper_homed_position_z + (
1554
+ self._labware.get_dimensions(labware_definition=labware_definition).z
1555
+ - self._labware.get_grip_height_from_labware_bottom(labware_definition)
1556
+ )
1557
+ # TODO(cb, 2024-01-18): Utilizing the nozzle map and labware X coordinates verify if collisions will occur on the X axis (analysis will use hard coded data to measure from the gripper critical point to the pipette mount)
1558
+ if (_PIPETTE_HOMED_POSITION_Z - tip.length) < labware_top_z_when_gripped:
1559
+ raise LabwareMovementNotAllowedError(
1560
+ f"Cannot move labware '{labware_definition.parameters.loadName}' when {int(tip.volume)} µL tips are attached."
1501
1561
  )
1502
- # TODO(cb, 2024-01-18): Utilizing the nozzle map and labware X coordinates verify if collisions will occur on the X axis (analysis will use hard coded data to measure from the gripper critical point to the pipette mount)
1503
- if (
1504
- _PIPETTE_HOMED_POSITION_Z - tip.length
1505
- ) < labware_top_z_when_gripped:
1506
- raise LabwareMovementNotAllowedError(
1507
- f"Cannot move labware '{labware_definition.parameters.loadName}' when {int(tip.volume)} µL tips are attached."
1508
- )
1509
1562
  return
1510
1563
 
1511
1564
  def _nominal_gripper_offsets_for_location(
@@ -930,10 +930,39 @@ class ModuleView:
930
930
  Includes the slot-specific transform. Does not include the child's
931
931
  Labware Position Check offset.
932
932
  """
933
- if (
934
- self._state.deck_type == DeckType.OT2_STANDARD
935
- or self._state.deck_type == DeckType.OT2_SHORT_TRASH
936
- ):
933
+ base = self.get_nominal_offset_to_child_from_addressable_area(module_id)
934
+ if self.get_deck_supports_module_fixtures():
935
+ module_addressable_area = self.get_provided_addressable_area(module_id)
936
+ module_addressable_area_position = (
937
+ addressable_areas.get_addressable_area_offsets_from_cutout(
938
+ module_addressable_area
939
+ )
940
+ )
941
+ return base + LabwareOffsetVector(
942
+ x=module_addressable_area_position.x,
943
+ y=module_addressable_area_position.y,
944
+ z=module_addressable_area_position.z,
945
+ )
946
+ else:
947
+ return base
948
+
949
+ def get_nominal_offset_to_child_from_addressable_area(
950
+ self, module_id: str
951
+ ) -> LabwareOffsetVector:
952
+ """Get the position offset for a child of this module from the nearest AA.
953
+
954
+ On the Flex, this is always (0, 0, 0); on the OT-2, since modules load on top
955
+ of addressable areas rather than providing addressable areas, the offset is
956
+ the labwareOffset from the module definition, rotated by the module's
957
+ slotTransform if appropriate.
958
+ """
959
+ if self.get_deck_supports_module_fixtures():
960
+ return LabwareOffsetVector(
961
+ x=0,
962
+ y=0,
963
+ z=0,
964
+ )
965
+ else:
937
966
  definition = self.get_definition(module_id)
938
967
  slot = self.get_location(module_id).slotName.id
939
968
 
@@ -968,18 +997,6 @@ class ModuleView:
968
997
  y=xformed[1],
969
998
  z=xformed[2],
970
999
  )
971
- else:
972
- module_addressable_area = self.get_provided_addressable_area(module_id)
973
- module_addressable_area_position = (
974
- addressable_areas.get_addressable_area_offsets_from_cutout(
975
- module_addressable_area
976
- )
977
- )
978
- return LabwareOffsetVector(
979
- x=module_addressable_area_position.x,
980
- y=module_addressable_area_position.y,
981
- z=module_addressable_area_position.z,
982
- )
983
1000
 
984
1001
  def get_module_calibration_offset(
985
1002
  self, module_id: str
@@ -253,6 +253,38 @@ class ModuleOffsetVector(BaseModel):
253
253
  y: float
254
254
  z: float
255
255
 
256
+ def __add__(self, other: Any) -> ModuleOffsetVector:
257
+ """Adds two vectors together."""
258
+ if not isinstance(other, (LabwareOffsetVector, ModuleOffsetVector)):
259
+ return NotImplemented
260
+ return ModuleOffsetVector(
261
+ x=self.x + other.x, y=self.y + other.y, z=self.z + other.z
262
+ )
263
+
264
+ def __radd__(self, other: Any) -> ModuleOffsetVector:
265
+ """Adds two vectors together, the other way."""
266
+ if not isinstance(other, (LabwareOffsetVector, ModuleOffsetVector)):
267
+ return NotImplemented
268
+ return ModuleOffsetVector(
269
+ x=other.x + self.x, y=other.y + self.y, z=other.z + self.z
270
+ )
271
+
272
+ def __sub__(self, other: Any) -> ModuleOffsetVector:
273
+ """Subtracts two vectors."""
274
+ if not isinstance(other, (LabwareOffsetVector, ModuleOffsetVector)):
275
+ return NotImplemented
276
+ return ModuleOffsetVector(
277
+ x=self.x - other.x, y=self.y - other.y, z=self.z - other.z
278
+ )
279
+
280
+ def __rsub__(self, other: Any) -> ModuleOffsetVector:
281
+ """Subtracts two vectors, the other way."""
282
+ if not isinstance(other, (LabwareOffsetVector, ModuleOffsetVector)):
283
+ return NotImplemented
284
+ return ModuleOffsetVector(
285
+ x=other.x - self.x, y=other.y - self.y, z=other.z - self.z
286
+ )
287
+
256
288
 
257
289
  @dataclass
258
290
  class ModuleOffsetData:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: opentrons
3
- Version: 8.4.0a7
3
+ Version: 8.4.0a8
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.4.0a7)
24
+ Requires-Dist: opentrons-shared-data (==8.4.0a8)
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)
@@ -35,9 +35,9 @@ Requires-Dist: pyusb (==1.2.1)
35
35
  Requires-Dist: packaging (>=21.0)
36
36
  Requires-Dist: importlib-metadata (>=1.0) ; python_version < "3.8"
37
37
  Provides-Extra: flex-hardware
38
- Requires-Dist: opentrons-hardware[flex] (==8.4.0a7) ; extra == 'flex-hardware'
38
+ Requires-Dist: opentrons-hardware[flex] (==8.4.0a8) ; extra == 'flex-hardware'
39
39
  Provides-Extra: ot2-hardware
40
- Requires-Dist: opentrons-hardware (==8.4.0a7) ; extra == 'ot2-hardware'
40
+ Requires-Dist: opentrons-hardware (==8.4.0a8) ; extra == 'ot2-hardware'
41
41
 
42
42
  .. _Full API Documentation: http://docs.opentrons.com
43
43
 
@@ -228,7 +228,7 @@ opentrons/protocol_api/config.py,sha256=r9lyvXjagTX_g3q5FGURPpcz2IA9sSF7Oa_1mKx-
228
228
  opentrons/protocol_api/create_protocol_context.py,sha256=wwsZje0L__oDnu1Yrihau320_f-ASloR9eL1QCtkOh8,7612
229
229
  opentrons/protocol_api/deck.py,sha256=94vFceg1SC1bAGd7TvC1ZpYwnJR-VlzurEZ6jkacYeg,8910
230
230
  opentrons/protocol_api/disposal_locations.py,sha256=NRiSGmDR0LnbyEkWSOM-o64uR2fUoB1NWJG7Y7SsJSs,7920
231
- opentrons/protocol_api/instrument_context.py,sha256=-X5RM0jAiplHNt5xr1Giarch1airL7nAhqMZWm_XATg,120139
231
+ opentrons/protocol_api/instrument_context.py,sha256=wqQ_un5E_sLjhwwNsp2qF_GTD0lajlrBG68JE7WgbPY,120913
232
232
  opentrons/protocol_api/labware.py,sha256=KRt91dOzmCX6l8LQ5Wny7kIk_L0Y69Uzpolp-geE0MY,60458
233
233
  opentrons/protocol_api/module_contexts.py,sha256=3tVXj6Q7n-WuTJPU_dvIQLzzGv1P-jsMuDtMVpuhAf8,48291
234
234
  opentrons/protocol_api/module_validation_and_errors.py,sha256=XL_m72P8rcvGO2fynY7UzXLcpGuI6X4s0V6Xf735Iyc,1464
@@ -238,7 +238,7 @@ opentrons/protocol_api/validation.py,sha256=uiVTHyJF3wSh5LLfaIDBTELoMNCAT17E767u
238
238
  opentrons/protocol_api/core/__init__.py,sha256=-g74o8OtBB0LmmOvwkRvPgrHt7fF7T8FRHDj-x_-Onk,736
239
239
  opentrons/protocol_api/core/common.py,sha256=q9ZbfpRdBvB3iDAOCyONtupvkYP5n1hjE-bwqGcwP_U,1172
240
240
  opentrons/protocol_api/core/core_map.py,sha256=gq3CIYPxuPvozf8yj8FprqBfs3e4ZJGQ6s0ViPbwV08,1757
241
- opentrons/protocol_api/core/instrument.py,sha256=MQHI1_MkrtrKM9lN-7BsVQ-xNc4on39TRr8M3HB3WPY,13705
241
+ opentrons/protocol_api/core/instrument.py,sha256=iDYCzLtO5fOpbS4dOneMsYFovYjS-GCirHm3jDZSUq4,13712
242
242
  opentrons/protocol_api/core/labware.py,sha256=-ZOjkalikXCV3ptehKCNaWGAdKxIdwne8LRFQW9NAm4,4290
243
243
  opentrons/protocol_api/core/module.py,sha256=z2STDyqqxZX3y6UyJVDnajeFXMEn1ie2NRBYHhry_XE,13838
244
244
  opentrons/protocol_api/core/protocol.py,sha256=v7v28jfeHSfOf-tqFDW2chGtrEatPiZ1y6YNwHfmtAs,9058
@@ -248,7 +248,7 @@ opentrons/protocol_api/core/well_grid.py,sha256=BU28DKaBgEU_JdZ6pEzrwNxmuh6TkO4z
248
248
  opentrons/protocol_api/core/engine/__init__.py,sha256=B_5T7zgkWDb1mXPg4NbT-wBkQaK-WVokMMnJRNu7xiM,582
249
249
  opentrons/protocol_api/core/engine/deck_conflict.py,sha256=q3JViIAHDthIqq6ce7h2gxw3CHRfYsm5kkwzuXB-Gnc,12334
250
250
  opentrons/protocol_api/core/engine/exceptions.py,sha256=aZgNrmYEeuPZm21nX_KZYtvyjv5h_zPjxxgPkEV7_bw,725
251
- opentrons/protocol_api/core/engine/instrument.py,sha256=qkoNv0KO_jQZffQDV4ixU2r-CkLIJl5IOz1dgs1G5V8,95869
251
+ opentrons/protocol_api/core/engine/instrument.py,sha256=jh3QIqGYDFMoJIrgZLq-IR2liqEhq5aCQgh5xomW3P8,95884
252
252
  opentrons/protocol_api/core/engine/labware.py,sha256=1xvzguNnK7aecFLiJK0gtRrZ5kpwtzLS73HnKvdJ5lc,8413
253
253
  opentrons/protocol_api/core/engine/load_labware_params.py,sha256=I4Cb8rqpBhmykQuZE8QRG802APrdCy_TYS88rm_9oGA,7159
254
254
  opentrons/protocol_api/core/engine/module_core.py,sha256=MLPgYSRJHUZPZ9rTLvsg3GlpL5b6-Pjk5UBgXCGrL6U,30994
@@ -263,7 +263,7 @@ opentrons/protocol_api/core/engine/well.py,sha256=o4Qm25AvZwd4ytR3IpK4wH-BwAmzt_
263
263
  opentrons/protocol_api/core/legacy/__init__.py,sha256=_9jCJNKG3SlS_vljVu8HHkZmtLf4F-f-JHALLF5d5go,401
264
264
  opentrons/protocol_api/core/legacy/deck.py,sha256=qHqcGo-Kdkl9L1aOE0pwrm9tsAnwkXbt4rIOr_VEP-s,13955
265
265
  opentrons/protocol_api/core/legacy/labware_offset_provider.py,sha256=2DLIby9xmUrwLb2ht8hZbvNTxqPhNzWijd7yCb2cqP8,3783
266
- opentrons/protocol_api/core/legacy/legacy_instrument_core.py,sha256=k-aM8Eu4qHqfhb8iRyn3jn3dZOGiq8JzQes8YUwY2v4,26501
266
+ opentrons/protocol_api/core/legacy/legacy_instrument_core.py,sha256=MMB5t77J8AceBa58Ri2NBA9jUfibEFr8IwkWpacg2BM,26508
267
267
  opentrons/protocol_api/core/legacy/legacy_labware_core.py,sha256=WQOgtMlq--zv0Ch7mmraYr9rQBT4ie2zHqwgamBq9J8,8606
268
268
  opentrons/protocol_api/core/legacy/legacy_module_core.py,sha256=tUhj88NKBMjCmCg6wjh1e2HX4d5hxjh8ZeJiYXaTaGY,23111
269
269
  opentrons/protocol_api/core/legacy/legacy_protocol_core.py,sha256=ZIFC7W6YA61oWWkq5xYGTcI_2S2pmALz16uB1R8HVyQ,23670
@@ -272,7 +272,7 @@ opentrons/protocol_api/core/legacy/load_info.py,sha256=r-WaH5ZJb3TRCp_zvbMMh0P4B
272
272
  opentrons/protocol_api/core/legacy/module_geometry.py,sha256=lvWFHZ81-JFw-1VZUW1R3yUIb59xpXT6H3jwlRintRo,21082
273
273
  opentrons/protocol_api/core/legacy/well_geometry.py,sha256=n5bEsvYZXXTAqYSAqlXd5t40bUPPrJ2Oj2frBZafQHA,4719
274
274
  opentrons/protocol_api/core/legacy_simulator/__init__.py,sha256=m9bLHGDJ6LSYC2WPm8tpOuu0zWSOPIrlybQgjRQBw9k,647
275
- opentrons/protocol_api/core/legacy_simulator/legacy_instrument_core.py,sha256=pV-PEw8jMcdXMi2U5yplIkFJUUlugXd7OMJl_riiVf4,22940
275
+ opentrons/protocol_api/core/legacy_simulator/legacy_instrument_core.py,sha256=j_EAItSq1mZ61qcRwacDDsXS9tJzG6rDtX8OQOFynG8,22947
276
276
  opentrons/protocol_api/core/legacy_simulator/legacy_protocol_core.py,sha256=28HrrHzeUfnGKXpZqQ-VM8WbPiadqVhKj2S9y33q6Lo,2910
277
277
  opentrons/protocol_engine/__init__.py,sha256=UPSk7MbidkiSH_h4V3yxMvyTePKpRr5DM9-wfkJrlSo,4094
278
278
  opentrons/protocol_engine/create_protocol_engine.py,sha256=tfDIsC7_JKlRiCXPB_8tuxRsssU6o0ViRmWbGPtX9QA,7582
@@ -314,7 +314,7 @@ opentrons/protocol_engine/commands/get_tip_presence.py,sha256=vK80XN5qCzK44SOSxH
314
314
  opentrons/protocol_engine/commands/hash_command_params.py,sha256=obWy4TbVH97SyhNqrSD6iP1wgZ20JoaH1rilZCjXxIs,1530
315
315
  opentrons/protocol_engine/commands/home.py,sha256=g77hewv-puMxEHFy0PPnl8-Nkbb7K-2qk36HbWG_Nik,3326
316
316
  opentrons/protocol_engine/commands/labware_handling_common.py,sha256=WtkdGIjQ5GBiBWsenyLyPLkSn6phgoesfWxCFTmG1AU,1074
317
- opentrons/protocol_engine/commands/liquid_probe.py,sha256=XbpvkjvwesToLsYmzYUrf7vJ_-13wo-irmEfIMEB5SU,15701
317
+ opentrons/protocol_engine/commands/liquid_probe.py,sha256=wjD-a_xUasEAQSfigexcIJ0FkjhEejlFUh6hpm-Y-JA,15899
318
318
  opentrons/protocol_engine/commands/load_labware.py,sha256=p02iOEbnYtq5gklOKTF2RNmYLVR4Ak7lnEUpSZjaHYc,8625
319
319
  opentrons/protocol_engine/commands/load_lid.py,sha256=8zRWBfdAQHSULLtjzYPEflkXC_rSSLZML9vJ4TJjxoQ,5484
320
320
  opentrons/protocol_engine/commands/load_lid_stack.py,sha256=WgSBr9bd4rRwv6yYztYuz-mIHMByPom6G0Vzo4znpAY,10572
@@ -336,11 +336,11 @@ opentrons/protocol_engine/commands/pressure_dispense.py,sha256=mGCJDFKwy6MPHg-AO
336
336
  opentrons/protocol_engine/commands/reload_labware.py,sha256=eB1X7M9rHso1qc0beBTVHm5SHbjS6IODsIDj9RhR3mI,2910
337
337
  opentrons/protocol_engine/commands/retract_axis.py,sha256=3h0eb6O3fjrCBY89q9tnjDFUGQXN9qafURxvXGcmCm4,2889
338
338
  opentrons/protocol_engine/commands/save_position.py,sha256=koxPh6t8s7Cl1vjPt9e6raZz5_rQtXsg_IGdWqAPzPI,3395
339
- opentrons/protocol_engine/commands/seal_pipette_to_tip.py,sha256=Ggflnbe1nxzILxGARIqKbbs9clBAe124ZtovGoHvDcI,11423
339
+ opentrons/protocol_engine/commands/seal_pipette_to_tip.py,sha256=xGCcoYbkSwRSPsuZm4idK5UQgdaAvVDtB3_JKwZ8Wj0,12290
340
340
  opentrons/protocol_engine/commands/set_rail_lights.py,sha256=QfB-NKw2ktBvSge1jOi9wYVLD19Vj6HiKw22Fsn0aSo,2090
341
341
  opentrons/protocol_engine/commands/set_status_bar.py,sha256=LJGFBteL8bD8tG8yMPQir93mWnGYlPy3er00THbp6kk,2834
342
342
  opentrons/protocol_engine/commands/touch_tip.py,sha256=620D8mXSKQdjXQonNVztz_ENOqjuHQd_oa-nHFiM2Ls,5713
343
- opentrons/protocol_engine/commands/unseal_pipette_from_tip.py,sha256=EtLcgbfUiUeFS2vovDFQdTyTk7KIwJI4XcmGqpr28wA,4915
343
+ opentrons/protocol_engine/commands/unseal_pipette_from_tip.py,sha256=5U-wkUR13CFt3KEK-j2ppT7d7_W8H8tOi1WT3xwJf-I,4768
344
344
  opentrons/protocol_engine/commands/verify_tip_presence.py,sha256=UUwUWdlryfh-mU8IoHUZkoy67KxrqXTFygeu1bsavhc,3136
345
345
  opentrons/protocol_engine/commands/wait_for_duration.py,sha256=G-Lmo97BoqW8KA-cZDGQkqUmEE7lrLdw5j-2_LBF5gg,2396
346
346
  opentrons/protocol_engine/commands/wait_for_resume.py,sha256=IE_I7fqoKBrlBumaCp5Tm5ihyA6i5VAzG25xdVdRnVw,2372
@@ -404,9 +404,9 @@ opentrons/protocol_engine/commands/unsafe/unsafe_engage_axes.py,sha256=Ze5hLslsX
404
404
  opentrons/protocol_engine/commands/unsafe/unsafe_place_labware.py,sha256=mcIPhcQvVCUL_wfb2-JPrCvIFTXMVD93VeJb4H1LmhM,7873
405
405
  opentrons/protocol_engine/commands/unsafe/unsafe_ungrip_labware.py,sha256=9Sjx92JZhi-tRY1P77M--jJp4JzIGESXUakIpstESwQ,2461
406
406
  opentrons/protocol_engine/commands/unsafe/update_position_estimators.py,sha256=xLe3u3rYu1eOvWxDtA2xsvNy7H6TVtljqo-78nOtFLI,2994
407
- opentrons/protocol_engine/errors/__init__.py,sha256=IaOHaCOTXwSNSG_QOVEgsXga0YHMAMw9sYwFLduDsGI,5952
407
+ opentrons/protocol_engine/errors/__init__.py,sha256=mqgb4cg9dAbi9YnKIUpbacX6lZiGNyWwEWKjpAen5YQ,6020
408
408
  opentrons/protocol_engine/errors/error_occurrence.py,sha256=ODyXHxVO4iXDxpcLaC3uO7ocTOOGPqWwcC1uaiytv0c,7846
409
- opentrons/protocol_engine/errors/exceptions.py,sha256=aDJBWiCuEHkN2yI06ZQn9z0_u0KO9DniE7_hRgrLNHA,45210
409
+ opentrons/protocol_engine/errors/exceptions.py,sha256=z45LKW0FzT7uNc6o9Y8-lkdPCuY6ChkSsbtsZsqMgrU,45610
410
410
  opentrons/protocol_engine/execution/__init__.py,sha256=X8qTYYOc1v84JIDM1xYpIo3VG_BPnsrfl2jm9UUQGwQ,1384
411
411
  opentrons/protocol_engine/execution/command_executor.py,sha256=SxE6W9_8HgiCLiPu6Q8hJr2HKsO1Ta-V1PdmFDxa7rQ,8208
412
412
  opentrons/protocol_engine/execution/create_queue_worker.py,sha256=6hMuOqbqwfMn63idsrAgro0NJZoyX4VVH7AV3le2EWM,3372
@@ -418,7 +418,7 @@ opentrons/protocol_engine/execution/hardware_stopper.py,sha256=wlIl7U3gvnOiCvwri
418
418
  opentrons/protocol_engine/execution/heater_shaker_movement_flagger.py,sha256=BSFLzSSeELAYZCrCUfJZx5DdlrwU06Ur92TYd0T-hzM,9084
419
419
  opentrons/protocol_engine/execution/labware_movement.py,sha256=XYVcxZOQ6_udpxcpwkIJpVD8-lgWLhizJAcRD9BclIo,12247
420
420
  opentrons/protocol_engine/execution/movement.py,sha256=AWcj7xlrOh3QrvoaH0sZO63yrPCEc7VE8hKfJNxxtP0,12527
421
- opentrons/protocol_engine/execution/pipetting.py,sha256=96ubBr_lCkfYieD8NheSKeiVoxXepVDcAZH1qCajDmc,22164
421
+ opentrons/protocol_engine/execution/pipetting.py,sha256=cnJYbLiJ2QD1xziD8dkRm0mZG3xOk00klW8Ff8rgSG4,22199
422
422
  opentrons/protocol_engine/execution/queue_worker.py,sha256=riVVywKIOQ3Lx-woFuuSqqBtfeKFt23nCUnsk7gSVoI,2860
423
423
  opentrons/protocol_engine/execution/rail_lights.py,sha256=eiJT6oI_kFk7rFuFkZzISZiLNnpf7Kkh86Kyk9wQ_Jo,590
424
424
  opentrons/protocol_engine/execution/run_control.py,sha256=ksvI2zkguC4G3lR3HJgAF8uY1PNcaRfi7UOYu-oIZgo,1144
@@ -450,11 +450,11 @@ opentrons/protocol_engine/state/config.py,sha256=7jSGxC6Vqj1eA8fqZ2I3zjlxVXg8pxv
450
450
  opentrons/protocol_engine/state/files.py,sha256=w8xxxg8HY0RqKKEGSfHWfrjV54Gb02O3dwtisJ-9j8E,1753
451
451
  opentrons/protocol_engine/state/fluid_stack.py,sha256=uwkf0qYk1UX5iU52xmk-e3yLPK8OG-TtMCcBqrkVFpM,5932
452
452
  opentrons/protocol_engine/state/frustum_helpers.py,sha256=uRBLLR75Z_PnfVd-U7gPF3NeOALR3TgLNCojgxB4z0o,17343
453
- opentrons/protocol_engine/state/geometry.py,sha256=oTMORp6Zc96HQTsEA8kWAtnVJCfMLdqBMcwP5_wEsQQ,98552
453
+ opentrons/protocol_engine/state/geometry.py,sha256=REIucYpouMOUXpr3450ExaiNapnb-aZEzSl_a-95rWU,100878
454
454
  opentrons/protocol_engine/state/labware.py,sha256=rehy7R1HIhxx3DTtTHIKxqHoBQJ_1tDhhiculMJeIy8,57556
455
455
  opentrons/protocol_engine/state/liquid_classes.py,sha256=u_z75UYdiFAKG0yB3mr1il4T3qaS0Sotq8sL7KLODP8,2990
456
456
  opentrons/protocol_engine/state/liquids.py,sha256=NoesktcQdJUjIVmet1uqqJPf-rzbo4SGemXwQC295W0,2338
457
- opentrons/protocol_engine/state/modules.py,sha256=wmCtpfc5tcp3LTH1oDfqeCgM2MJHxiN5z30H8im1FGw,58084
457
+ opentrons/protocol_engine/state/modules.py,sha256=U3CUhV0KOz_eKBED5QNEanWq4JICqrzHdTbmV3nDouM,58786
458
458
  opentrons/protocol_engine/state/motion.py,sha256=pWt28-vBaO6dz_rtvoliUsGDvls_ML6sRfDOwmD0F7g,15086
459
459
  opentrons/protocol_engine/state/pipettes.py,sha256=nqeJWn98S8wSDW-CPJnjEl5hEIkmhZOInmDzn0-kxDM,36921
460
460
  opentrons/protocol_engine/state/state.py,sha256=gHmXrlq17kx3sTbizq8gPoJQx_0nazxnooLODvrLY5k,15468
@@ -487,7 +487,7 @@ opentrons/protocol_engine/types/liquid_class.py,sha256=SF5WS3s38S87efUqawRGSIYqj
487
487
  opentrons/protocol_engine/types/liquid_handling.py,sha256=Xx1GihrNRJJdJJA5zIwWvIYNydbSXAHjSUAliF18Iu0,319
488
488
  opentrons/protocol_engine/types/liquid_level_detection.py,sha256=0Pf9B_w6tTZ110pGbjgZVoigmOIEF0DVuXVHvhRKeNQ,4395
489
489
  opentrons/protocol_engine/types/location.py,sha256=qIYBs86RO1ws2aXStmdx0CqEVNF9enlj-ACknf75nSs,5707
490
- opentrons/protocol_engine/types/module.py,sha256=su7Qw5Z0o-qAGfz0XC6nOqJpgpe84Kx96E9oHUt7Q9I,8921
490
+ opentrons/protocol_engine/types/module.py,sha256=E2tQVGRBPL9DCbbY8dfC4PQafRz-ws6UJRkF9i-3q9Q,10249
491
491
  opentrons/protocol_engine/types/partial_tip_configuration.py,sha256=4RMtHOAX-dgpXWA737tthj_izTBnhKphBcA24LAKmhI,2760
492
492
  opentrons/protocol_engine/types/run_time_parameters.py,sha256=5gH4GmGK7e3OkSClftZT6VA4wXELIvEMgpmQ__waceU,4468
493
493
  opentrons/protocol_engine/types/tip.py,sha256=qfjnCPG7RAlTGS80SAQB8rGwtFW3yRWYj7DpYy0oDow,389
@@ -583,9 +583,9 @@ opentrons/util/linal.py,sha256=IlKAP9HkNBBgULeSf4YVwSKHdx9jnCjSr7nvDvlRALg,5753
583
583
  opentrons/util/logging_config.py,sha256=7et4YYuQdWdq_e50U-8vFS_QyNBRgdnqPGAQJm8qrIo,9954
584
584
  opentrons/util/logging_queue_handler.py,sha256=ZsSJwy-oV8DXwpYiZisQ1PbYwmK2cOslD46AcyJ1E4I,2484
585
585
  opentrons/util/performance_helpers.py,sha256=ew7H8XD20iS6-2TJAzbQeyzStZkkE6PzHt_Adx3wbZQ,5172
586
- opentrons-8.4.0a7.dist-info/LICENSE,sha256=z8d0m5b2O9McPEK1xHG_dWgUBT6EfBDz6wA0F7xSPTA,11358
587
- opentrons-8.4.0a7.dist-info/METADATA,sha256=IcoTsLV3w0JPwCTAuaSs0jj8PUFnhR8M1RTdrygSaEw,5084
588
- opentrons-8.4.0a7.dist-info/WHEEL,sha256=qUzzGenXXuJTzyjFah76kDVqDvnk-YDzY00svnrl84w,109
589
- opentrons-8.4.0a7.dist-info/entry_points.txt,sha256=fTa6eGCYkvOtv0ov-KVE8LLGetgb35LQLF9x85OWPVw,106
590
- opentrons-8.4.0a7.dist-info/top_level.txt,sha256=wk6whpbMZdBQpcK0Fg0YVfUGrAgVOFON7oQAhOMGMW8,10
591
- opentrons-8.4.0a7.dist-info/RECORD,,
586
+ opentrons-8.4.0a8.dist-info/LICENSE,sha256=z8d0m5b2O9McPEK1xHG_dWgUBT6EfBDz6wA0F7xSPTA,11358
587
+ opentrons-8.4.0a8.dist-info/METADATA,sha256=9ntyEPWku4FwnS7WhzXREh1pQgG-LVLXuLFWUMhRk_A,5084
588
+ opentrons-8.4.0a8.dist-info/WHEEL,sha256=qUzzGenXXuJTzyjFah76kDVqDvnk-YDzY00svnrl84w,109
589
+ opentrons-8.4.0a8.dist-info/entry_points.txt,sha256=fTa6eGCYkvOtv0ov-KVE8LLGetgb35LQLF9x85OWPVw,106
590
+ opentrons-8.4.0a8.dist-info/top_level.txt,sha256=wk6whpbMZdBQpcK0Fg0YVfUGrAgVOFON7oQAhOMGMW8,10
591
+ opentrons-8.4.0a8.dist-info/RECORD,,