opentrons 8.3.2__py2.py3-none-any.whl → 8.4.0__py2.py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of opentrons might be problematic. Click here for more details.

Files changed (196) hide show
  1. opentrons/calibration_storage/ot2/mark_bad_calibration.py +2 -0
  2. opentrons/calibration_storage/ot2/tip_length.py +6 -6
  3. opentrons/config/advanced_settings.py +9 -11
  4. opentrons/config/feature_flags.py +0 -4
  5. opentrons/config/reset.py +7 -2
  6. opentrons/drivers/asyncio/communication/__init__.py +2 -0
  7. opentrons/drivers/asyncio/communication/async_serial.py +4 -0
  8. opentrons/drivers/asyncio/communication/errors.py +41 -8
  9. opentrons/drivers/asyncio/communication/serial_connection.py +36 -10
  10. opentrons/drivers/flex_stacker/__init__.py +9 -3
  11. opentrons/drivers/flex_stacker/abstract.py +140 -15
  12. opentrons/drivers/flex_stacker/driver.py +593 -47
  13. opentrons/drivers/flex_stacker/errors.py +64 -0
  14. opentrons/drivers/flex_stacker/simulator.py +222 -24
  15. opentrons/drivers/flex_stacker/types.py +211 -15
  16. opentrons/drivers/flex_stacker/utils.py +19 -0
  17. opentrons/execute.py +4 -2
  18. opentrons/hardware_control/api.py +5 -0
  19. opentrons/hardware_control/backends/flex_protocol.py +4 -0
  20. opentrons/hardware_control/backends/ot3controller.py +12 -1
  21. opentrons/hardware_control/backends/ot3simulator.py +3 -0
  22. opentrons/hardware_control/backends/subsystem_manager.py +8 -4
  23. opentrons/hardware_control/instruments/ot2/instrument_calibration.py +10 -6
  24. opentrons/hardware_control/instruments/ot3/pipette_handler.py +59 -6
  25. opentrons/hardware_control/modules/__init__.py +12 -1
  26. opentrons/hardware_control/modules/absorbance_reader.py +11 -9
  27. opentrons/hardware_control/modules/flex_stacker.py +498 -0
  28. opentrons/hardware_control/modules/heater_shaker.py +12 -10
  29. opentrons/hardware_control/modules/magdeck.py +5 -1
  30. opentrons/hardware_control/modules/tempdeck.py +5 -1
  31. opentrons/hardware_control/modules/thermocycler.py +15 -14
  32. opentrons/hardware_control/modules/types.py +191 -1
  33. opentrons/hardware_control/modules/utils.py +3 -0
  34. opentrons/hardware_control/motion_utilities.py +20 -0
  35. opentrons/hardware_control/ot3api.py +145 -15
  36. opentrons/hardware_control/protocols/liquid_handler.py +47 -1
  37. opentrons/hardware_control/types.py +6 -0
  38. opentrons/legacy_commands/commands.py +102 -5
  39. opentrons/legacy_commands/helpers.py +74 -1
  40. opentrons/legacy_commands/types.py +33 -2
  41. opentrons/protocol_api/__init__.py +2 -0
  42. opentrons/protocol_api/_liquid.py +39 -8
  43. opentrons/protocol_api/_liquid_properties.py +20 -19
  44. opentrons/protocol_api/_transfer_liquid_validation.py +91 -0
  45. opentrons/protocol_api/core/common.py +3 -1
  46. opentrons/protocol_api/core/engine/deck_conflict.py +11 -1
  47. opentrons/protocol_api/core/engine/instrument.py +1356 -107
  48. opentrons/protocol_api/core/engine/labware.py +8 -4
  49. opentrons/protocol_api/core/engine/load_labware_params.py +68 -10
  50. opentrons/protocol_api/core/engine/module_core.py +118 -2
  51. opentrons/protocol_api/core/engine/pipette_movement_conflict.py +6 -14
  52. opentrons/protocol_api/core/engine/protocol.py +253 -11
  53. opentrons/protocol_api/core/engine/stringify.py +19 -8
  54. opentrons/protocol_api/core/engine/transfer_components_executor.py +858 -0
  55. opentrons/protocol_api/core/engine/well.py +73 -5
  56. opentrons/protocol_api/core/instrument.py +71 -21
  57. opentrons/protocol_api/core/labware.py +6 -2
  58. opentrons/protocol_api/core/legacy/labware_offset_provider.py +7 -3
  59. opentrons/protocol_api/core/legacy/legacy_instrument_core.py +76 -49
  60. opentrons/protocol_api/core/legacy/legacy_labware_core.py +8 -4
  61. opentrons/protocol_api/core/legacy/legacy_protocol_core.py +36 -0
  62. opentrons/protocol_api/core/legacy/legacy_well_core.py +27 -2
  63. opentrons/protocol_api/core/legacy/load_info.py +4 -12
  64. opentrons/protocol_api/core/legacy/module_geometry.py +6 -1
  65. opentrons/protocol_api/core/legacy/well_geometry.py +3 -3
  66. opentrons/protocol_api/core/legacy_simulator/legacy_instrument_core.py +73 -23
  67. opentrons/protocol_api/core/module.py +43 -0
  68. opentrons/protocol_api/core/protocol.py +33 -0
  69. opentrons/protocol_api/core/well.py +23 -2
  70. opentrons/protocol_api/instrument_context.py +454 -150
  71. opentrons/protocol_api/labware.py +98 -50
  72. opentrons/protocol_api/module_contexts.py +140 -0
  73. opentrons/protocol_api/protocol_context.py +163 -19
  74. opentrons/protocol_api/validation.py +51 -41
  75. opentrons/protocol_engine/__init__.py +21 -2
  76. opentrons/protocol_engine/actions/actions.py +5 -5
  77. opentrons/protocol_engine/clients/sync_client.py +6 -0
  78. opentrons/protocol_engine/commands/__init__.py +66 -36
  79. opentrons/protocol_engine/commands/absorbance_reader/__init__.py +0 -1
  80. opentrons/protocol_engine/commands/air_gap_in_place.py +3 -2
  81. opentrons/protocol_engine/commands/aspirate.py +6 -2
  82. opentrons/protocol_engine/commands/aspirate_in_place.py +3 -1
  83. opentrons/protocol_engine/commands/aspirate_while_tracking.py +210 -0
  84. opentrons/protocol_engine/commands/blow_out.py +2 -0
  85. opentrons/protocol_engine/commands/blow_out_in_place.py +4 -1
  86. opentrons/protocol_engine/commands/command_unions.py +102 -33
  87. opentrons/protocol_engine/commands/configure_for_volume.py +3 -0
  88. opentrons/protocol_engine/commands/dispense.py +3 -1
  89. opentrons/protocol_engine/commands/dispense_in_place.py +3 -0
  90. opentrons/protocol_engine/commands/dispense_while_tracking.py +204 -0
  91. opentrons/protocol_engine/commands/drop_tip.py +23 -1
  92. opentrons/protocol_engine/commands/flex_stacker/__init__.py +106 -0
  93. opentrons/protocol_engine/commands/flex_stacker/close_latch.py +72 -0
  94. opentrons/protocol_engine/commands/flex_stacker/common.py +15 -0
  95. opentrons/protocol_engine/commands/flex_stacker/empty.py +161 -0
  96. opentrons/protocol_engine/commands/flex_stacker/fill.py +164 -0
  97. opentrons/protocol_engine/commands/flex_stacker/open_latch.py +70 -0
  98. opentrons/protocol_engine/commands/flex_stacker/prepare_shuttle.py +112 -0
  99. opentrons/protocol_engine/commands/flex_stacker/retrieve.py +394 -0
  100. opentrons/protocol_engine/commands/flex_stacker/set_stored_labware.py +190 -0
  101. opentrons/protocol_engine/commands/flex_stacker/store.py +291 -0
  102. opentrons/protocol_engine/commands/generate_command_schema.py +31 -2
  103. opentrons/protocol_engine/commands/labware_handling_common.py +29 -0
  104. opentrons/protocol_engine/commands/liquid_probe.py +27 -13
  105. opentrons/protocol_engine/commands/load_labware.py +42 -39
  106. opentrons/protocol_engine/commands/load_lid.py +21 -13
  107. opentrons/protocol_engine/commands/load_lid_stack.py +130 -47
  108. opentrons/protocol_engine/commands/load_module.py +18 -17
  109. opentrons/protocol_engine/commands/load_pipette.py +3 -0
  110. opentrons/protocol_engine/commands/move_labware.py +139 -20
  111. opentrons/protocol_engine/commands/move_to_well.py +5 -11
  112. opentrons/protocol_engine/commands/pick_up_tip.py +5 -2
  113. opentrons/protocol_engine/commands/pipetting_common.py +159 -8
  114. opentrons/protocol_engine/commands/prepare_to_aspirate.py +15 -5
  115. opentrons/protocol_engine/commands/{evotip_dispense.py → pressure_dispense.py} +33 -34
  116. opentrons/protocol_engine/commands/reload_labware.py +6 -19
  117. opentrons/protocol_engine/commands/{evotip_seal_pipette.py → seal_pipette_to_tip.py} +97 -76
  118. opentrons/protocol_engine/commands/unsafe/unsafe_blow_out_in_place.py +3 -1
  119. opentrons/protocol_engine/commands/unsafe/unsafe_drop_tip_in_place.py +6 -1
  120. opentrons/protocol_engine/commands/{evotip_unseal_pipette.py → unseal_pipette_from_tip.py} +31 -40
  121. opentrons/protocol_engine/errors/__init__.py +10 -0
  122. opentrons/protocol_engine/errors/exceptions.py +62 -0
  123. opentrons/protocol_engine/execution/equipment.py +123 -106
  124. opentrons/protocol_engine/execution/labware_movement.py +8 -6
  125. opentrons/protocol_engine/execution/pipetting.py +235 -25
  126. opentrons/protocol_engine/execution/tip_handler.py +82 -32
  127. opentrons/protocol_engine/labware_offset_standardization.py +194 -0
  128. opentrons/protocol_engine/protocol_engine.py +22 -13
  129. opentrons/protocol_engine/resources/deck_configuration_provider.py +98 -2
  130. opentrons/protocol_engine/resources/deck_data_provider.py +1 -1
  131. opentrons/protocol_engine/resources/labware_data_provider.py +32 -12
  132. opentrons/protocol_engine/resources/labware_validation.py +7 -5
  133. opentrons/protocol_engine/slot_standardization.py +11 -23
  134. opentrons/protocol_engine/state/addressable_areas.py +84 -46
  135. opentrons/protocol_engine/state/frustum_helpers.py +36 -14
  136. opentrons/protocol_engine/state/geometry.py +892 -227
  137. opentrons/protocol_engine/state/labware.py +252 -55
  138. opentrons/protocol_engine/state/module_substates/__init__.py +4 -0
  139. opentrons/protocol_engine/state/module_substates/flex_stacker_substate.py +68 -0
  140. opentrons/protocol_engine/state/module_substates/heater_shaker_module_substate.py +22 -0
  141. opentrons/protocol_engine/state/module_substates/temperature_module_substate.py +13 -0
  142. opentrons/protocol_engine/state/module_substates/thermocycler_module_substate.py +20 -0
  143. opentrons/protocol_engine/state/modules.py +210 -67
  144. opentrons/protocol_engine/state/pipettes.py +54 -0
  145. opentrons/protocol_engine/state/state.py +1 -1
  146. opentrons/protocol_engine/state/tips.py +14 -0
  147. opentrons/protocol_engine/state/update_types.py +180 -25
  148. opentrons/protocol_engine/state/wells.py +55 -9
  149. opentrons/protocol_engine/types/__init__.py +300 -0
  150. opentrons/protocol_engine/types/automatic_tip_selection.py +39 -0
  151. opentrons/protocol_engine/types/command_annotations.py +53 -0
  152. opentrons/protocol_engine/types/deck_configuration.py +72 -0
  153. opentrons/protocol_engine/types/execution.py +96 -0
  154. opentrons/protocol_engine/types/hardware_passthrough.py +25 -0
  155. opentrons/protocol_engine/types/instrument.py +47 -0
  156. opentrons/protocol_engine/types/instrument_sensors.py +47 -0
  157. opentrons/protocol_engine/types/labware.py +111 -0
  158. opentrons/protocol_engine/types/labware_movement.py +22 -0
  159. opentrons/protocol_engine/types/labware_offset_location.py +111 -0
  160. opentrons/protocol_engine/types/labware_offset_vector.py +33 -0
  161. opentrons/protocol_engine/types/liquid.py +40 -0
  162. opentrons/protocol_engine/types/liquid_class.py +59 -0
  163. opentrons/protocol_engine/types/liquid_handling.py +13 -0
  164. opentrons/protocol_engine/types/liquid_level_detection.py +131 -0
  165. opentrons/protocol_engine/types/location.py +194 -0
  166. opentrons/protocol_engine/types/module.py +301 -0
  167. opentrons/protocol_engine/types/partial_tip_configuration.py +76 -0
  168. opentrons/protocol_engine/types/run_time_parameters.py +133 -0
  169. opentrons/protocol_engine/types/tip.py +18 -0
  170. opentrons/protocol_engine/types/util.py +21 -0
  171. opentrons/protocol_engine/types/well_position.py +124 -0
  172. opentrons/protocol_reader/extract_labware_definitions.py +7 -3
  173. opentrons/protocol_reader/file_format_validator.py +5 -3
  174. opentrons/protocol_runner/json_translator.py +4 -2
  175. opentrons/protocol_runner/legacy_command_mapper.py +6 -2
  176. opentrons/protocol_runner/run_orchestrator.py +4 -1
  177. opentrons/protocols/advanced_control/transfers/common.py +48 -1
  178. opentrons/protocols/advanced_control/transfers/transfer_liquid_utils.py +204 -0
  179. opentrons/protocols/api_support/definitions.py +1 -1
  180. opentrons/protocols/api_support/instrument.py +16 -3
  181. opentrons/protocols/labware.py +27 -23
  182. opentrons/protocols/models/__init__.py +0 -21
  183. opentrons/simulate.py +4 -2
  184. opentrons/types.py +20 -7
  185. opentrons/util/logging_config.py +94 -25
  186. opentrons/util/logging_queue_handler.py +61 -0
  187. {opentrons-8.3.2.dist-info → opentrons-8.4.0.dist-info}/METADATA +4 -4
  188. {opentrons-8.3.2.dist-info → opentrons-8.4.0.dist-info}/RECORD +192 -151
  189. opentrons/calibration_storage/ot2/models/defaults.py +0 -0
  190. opentrons/calibration_storage/ot3/models/defaults.py +0 -0
  191. opentrons/protocol_api/core/legacy/legacy_robot_core.py +0 -0
  192. opentrons/protocol_engine/types.py +0 -1311
  193. {opentrons-8.3.2.dist-info → opentrons-8.4.0.dist-info}/LICENSE +0 -0
  194. {opentrons-8.3.2.dist-info → opentrons-8.4.0.dist-info}/WHEEL +0 -0
  195. {opentrons-8.3.2.dist-info → opentrons-8.4.0.dist-info}/entry_points.txt +0 -0
  196. {opentrons-8.3.2.dist-info → opentrons-8.4.0.dist-info}/top_level.txt +0 -0
@@ -1,5 +1,7 @@
1
1
  """Structures to represent changes that commands want to make to engine state."""
2
2
 
3
+ from __future__ import annotations
4
+
3
5
  import dataclasses
4
6
  import enum
5
7
  import typing
@@ -16,6 +18,7 @@ from opentrons.protocol_engine.types import (
16
18
  AspiratedFluid,
17
19
  LiquidClassRecord,
18
20
  ABSMeasureMode,
21
+ LiquidTrackingType,
19
22
  )
20
23
  from opentrons.types import MountType
21
24
  from opentrons_shared_data.labware.labware_definition import LabwareDefinition
@@ -58,6 +61,16 @@ Unfortunately, mypy doesn't let us write `Literal[CLEAR]`. Use this instead.
58
61
  """
59
62
 
60
63
 
64
+ class _SimulatedEnum(enum.Enum):
65
+ SIMULATED = enum.auto()
66
+
67
+
68
+ SIMULATED: typing.Final = _SimulatedEnum.SIMULATED
69
+ """A sentinel value to indicate that a liquid probe return value is simulated.
70
+
71
+ Useful to avoid throwing unnecessary errors in protocol analysis."""
72
+
73
+
61
74
  @dataclasses.dataclass(frozen=True)
62
75
  class Well:
63
76
  """Designates a well in a labware."""
@@ -104,6 +117,16 @@ class LabwareLocationUpdate:
104
117
  """The ID of the labware's new offset, for its new location."""
105
118
 
106
119
 
120
+ @dataclasses.dataclass
121
+ class BatchLabwareLocationUpdate:
122
+ """An update to the locations of multiple labware."""
123
+
124
+ new_locations_by_id: dict[str, LabwareLocation]
125
+ """The new locations of each ID."""
126
+ new_offset_ids_by_id: dict[str, str | None]
127
+ """The new offsets of each id."""
128
+
129
+
107
130
  @dataclasses.dataclass
108
131
  class LoadedLabwareUpdate:
109
132
  """An update that loads a new labware."""
@@ -122,6 +145,23 @@ class LoadedLabwareUpdate:
122
145
  definition: LabwareDefinition
123
146
 
124
147
 
148
+ @dataclasses.dataclass
149
+ class BatchLoadedLabwareUpdate:
150
+ """An update that loads multiple new labware."""
151
+
152
+ new_locations_by_id: typing.Dict[str, LabwareLocation]
153
+ """Each new labwares's initial location keyed by Labware ID."""
154
+
155
+ offset_ids_by_id: typing.Dict[str, str | None]
156
+ """The ID of each labware's offset keyed by labware ID."""
157
+
158
+ display_names_by_id: typing.Dict[str, str | None]
159
+ """The Display Name for each new labware keyed by labware ID"""
160
+
161
+ definitions_by_id: typing.Dict[str, LabwareDefinition]
162
+ """The Labware Definition for each labware keyed by Labware ID."""
163
+
164
+
125
165
  @dataclasses.dataclass
126
166
  class LoadedLidStackUpdate:
127
167
  """An update that loads a new lid stack."""
@@ -135,13 +175,10 @@ class LoadedLidStackUpdate:
135
175
  stack_location: LabwareLocation
136
176
  "The initial location of the Lid Stack Object."
137
177
 
138
- labware_ids: typing.List[str]
139
- """The unique IDs of the new lids."""
140
-
141
178
  new_locations_by_id: typing.Dict[str, OnLabwareLocation]
142
179
  """Each lid's initial location keyed by Labware ID."""
143
180
 
144
- definition: LabwareDefinition
181
+ definition: LabwareDefinition | None
145
182
  "The Labware Definition of the Lid Labware(s) loaded."
146
183
 
147
184
 
@@ -149,10 +186,10 @@ class LoadedLidStackUpdate:
149
186
  class LabwareLidUpdate:
150
187
  """An update that identifies a lid on a given parent labware."""
151
188
 
152
- parent_labware_id: str
153
- """The unique ID of the parent labware."""
189
+ parent_labware_ids: typing.List[str]
190
+ """The unique IDs of the parent labwares."""
154
191
 
155
- lid_id: str
192
+ lid_ids: typing.List[str | None]
156
193
  """The unique IDs of the new lids."""
157
194
 
158
195
 
@@ -201,6 +238,14 @@ class PipetteTipStateUpdate:
201
238
  tip_geometry: TipGeometry | None
202
239
 
203
240
 
241
+ @dataclasses.dataclass
242
+ class PipetteAspirateReadyUpdate:
243
+ """Update pipette ready state."""
244
+
245
+ pipette_id: str
246
+ ready_to_aspirate: bool
247
+
248
+
204
249
  @dataclasses.dataclass
205
250
  class TipsUsedUpdate:
206
251
  """Represents an update that marks tips in a tip rack as used."""
@@ -234,8 +279,8 @@ class LiquidProbedUpdate:
234
279
  labware_id: str
235
280
  well_name: str
236
281
  last_probed: datetime
237
- height: float | ClearType
238
- volume: float | ClearType
282
+ height: LiquidTrackingType | ClearType
283
+ volume: LiquidTrackingType | ClearType
239
284
 
240
285
 
241
286
  @dataclasses.dataclass
@@ -278,6 +323,7 @@ class PipetteEmptyFluidUpdate:
278
323
  """Sets the pipette to be valid and empty."""
279
324
 
280
325
  pipette_id: str
326
+ clean_tip: bool
281
327
  type: typing.Literal["empty"] = "empty"
282
328
 
283
329
 
@@ -311,9 +357,40 @@ class AbsorbanceReaderStateUpdate:
311
357
  module_id: str
312
358
  absorbance_reader_lid: AbsorbanceReaderLidUpdate | NoChangeType = NO_CHANGE
313
359
  absorbance_reader_data: AbsorbanceReaderDataUpdate | NoChangeType = NO_CHANGE
314
- initialize_absorbance_reader_update: AbsorbanceReaderInitializeUpdate | NoChangeType = (
315
- NO_CHANGE
316
- )
360
+ initialize_absorbance_reader_update: (
361
+ AbsorbanceReaderInitializeUpdate | NoChangeType
362
+ ) = NO_CHANGE
363
+
364
+
365
+ @dataclasses.dataclass
366
+ class FlexStackerPoolConstraint:
367
+ """The labware definitions that are contained in the pool."""
368
+
369
+ max_pool_count: int
370
+ primary_definition: LabwareDefinition
371
+ lid_definition: LabwareDefinition | None
372
+ adapter_definition: LabwareDefinition | None
373
+
374
+
375
+ @dataclasses.dataclass
376
+ class FlexStackerStateUpdate:
377
+ """An update to the Flex Stacker module state."""
378
+
379
+ module_id: str
380
+ pool_constraint: FlexStackerPoolConstraint | NoChangeType = NO_CHANGE
381
+ pool_count: int | NoChangeType = NO_CHANGE
382
+
383
+ @classmethod
384
+ def create_or_override(
385
+ cls,
386
+ maybe_inst: FlexStackerStateUpdate | NoChangeType,
387
+ module_id: str,
388
+ ) -> FlexStackerStateUpdate:
389
+ """Build or default a state update."""
390
+ if maybe_inst == NO_CHANGE:
391
+ return FlexStackerStateUpdate(module_id=module_id)
392
+ else:
393
+ return maybe_inst
317
394
 
318
395
 
319
396
  @dataclasses.dataclass
@@ -362,8 +439,12 @@ class StateUpdate:
362
439
 
363
440
  labware_location: LabwareLocationUpdate | NoChangeType = NO_CHANGE
364
441
 
442
+ batch_labware_location: BatchLabwareLocationUpdate | NoChangeType = NO_CHANGE
443
+
365
444
  loaded_labware: LoadedLabwareUpdate | NoChangeType = NO_CHANGE
366
445
 
446
+ batch_loaded_labware: BatchLoadedLabwareUpdate | NoChangeType = NO_CHANGE
447
+
367
448
  loaded_lid_stack: LoadedLidStackUpdate | NoChangeType = NO_CHANGE
368
449
 
369
450
  labware_lid: LabwareLidUpdate | NoChangeType = NO_CHANGE
@@ -380,12 +461,16 @@ class StateUpdate:
380
461
  NO_CHANGE
381
462
  )
382
463
 
464
+ flex_stacker_state_update: FlexStackerStateUpdate | NoChangeType = NO_CHANGE
465
+
383
466
  liquid_class_loaded: LiquidClassLoadedUpdate | NoChangeType = NO_CHANGE
384
467
 
385
468
  files_added: FilesAddedUpdate | NoChangeType = NO_CHANGE
386
469
 
387
470
  addressable_area_used: AddressableAreaUsedUpdate | NoChangeType = NO_CHANGE
388
471
 
472
+ ready_to_aspirate: PipetteAspirateReadyUpdate | NoChangeType = NO_CHANGE
473
+
389
474
  def append(self, other: Self) -> Self:
390
475
  """Apply another `StateUpdate` "on top of" this one.
391
476
 
@@ -493,6 +578,19 @@ class StateUpdate:
493
578
  )
494
579
  return self
495
580
 
581
+ def set_batch_labware_location(
582
+ self: Self,
583
+ *,
584
+ new_locations_by_id: typing.Dict[str, LabwareLocation],
585
+ new_offset_ids_by_id: typing.Dict[str, str | None],
586
+ ) -> Self:
587
+ """Update the location of multiple labware objects."""
588
+ self.batch_labware_location = BatchLabwareLocationUpdate(
589
+ new_locations_by_id=new_locations_by_id,
590
+ new_offset_ids_by_id=new_offset_ids_by_id,
591
+ )
592
+ return self
593
+
496
594
  def set_loaded_labware(
497
595
  self: Self,
498
596
  definition: LabwareDefinition,
@@ -511,14 +609,29 @@ class StateUpdate:
511
609
  )
512
610
  return self
513
611
 
612
+ def set_batch_loaded_labware(
613
+ self: Self,
614
+ definitions_by_id: typing.Dict[str, LabwareDefinition],
615
+ offset_ids_by_id: typing.Dict[str, str | None],
616
+ display_names_by_id: typing.Dict[str, str | None],
617
+ new_locations_by_id: typing.Dict[str, LabwareLocation],
618
+ ) -> Self:
619
+ """Add a set of new labwares to state. See `BatchLoadedLabwareUpdate`."""
620
+ self.batch_loaded_labware = BatchLoadedLabwareUpdate(
621
+ new_locations_by_id=new_locations_by_id,
622
+ offset_ids_by_id=offset_ids_by_id,
623
+ display_names_by_id=display_names_by_id,
624
+ definitions_by_id=definitions_by_id,
625
+ )
626
+ return self
627
+
514
628
  def set_loaded_lid_stack(
515
629
  self: Self,
516
630
  stack_id: str,
517
631
  stack_object_definition: LabwareDefinition,
518
632
  stack_location: LabwareLocation,
519
- labware_definition: LabwareDefinition,
520
- labware_ids: typing.List[str],
521
633
  locations: typing.Dict[str, OnLabwareLocation],
634
+ labware_definition: LabwareDefinition | None,
522
635
  ) -> Self:
523
636
  """Add a new lid stack to state. See `LoadedLidStackUpdate`."""
524
637
  self.loaded_lid_stack = LoadedLidStackUpdate(
@@ -526,20 +639,19 @@ class StateUpdate:
526
639
  stack_object_definition=stack_object_definition,
527
640
  stack_location=stack_location,
528
641
  definition=labware_definition,
529
- labware_ids=labware_ids,
530
642
  new_locations_by_id=locations,
531
643
  )
532
644
  return self
533
645
 
534
- def set_lid(
646
+ def set_lids(
535
647
  self: Self,
536
- parent_labware_id: str,
537
- lid_id: str,
648
+ parent_labware_ids: typing.List[str],
649
+ lid_ids: typing.List[str | None],
538
650
  ) -> Self:
539
651
  """Update the labware parent of a loaded or moved lid. See `LabwareLidUpdate`."""
540
652
  self.labware_lid = LabwareLidUpdate(
541
- parent_labware_id=parent_labware_id,
542
- lid_id=lid_id,
653
+ parent_labware_ids=parent_labware_ids,
654
+ lid_ids=lid_ids,
543
655
  )
544
656
  return self
545
657
 
@@ -617,8 +729,8 @@ class StateUpdate:
617
729
  labware_id: str,
618
730
  well_name: str,
619
731
  last_probed: datetime,
620
- height: float | ClearType,
621
- volume: float | ClearType,
732
+ height: LiquidTrackingType | ClearType,
733
+ volume: LiquidTrackingType | ClearType,
622
734
  ) -> Self:
623
735
  """Add a liquid height and volume to well state. See `ProbeLiquidUpdate`."""
624
736
  self.liquid_probed = LiquidProbedUpdate(
@@ -665,10 +777,10 @@ class StateUpdate:
665
777
  )
666
778
  return self
667
779
 
668
- def set_fluid_empty(self: Self, pipette_id: str) -> Self:
669
- """Update record fo fluid held inside a pipette. See `PipetteEmptyFluidUpdate`."""
780
+ def set_fluid_empty(self: Self, pipette_id: str, clean_tip: bool = False) -> Self:
781
+ """Update record of fluid held inside a pipette. See `PipetteEmptyFluidUpdate`."""
670
782
  self.pipette_aspirated_fluid = PipetteEmptyFluidUpdate(
671
- type="empty", pipette_id=pipette_id
783
+ type="empty", pipette_id=pipette_id, clean_tip=clean_tip
672
784
  )
673
785
  return self
674
786
 
@@ -717,3 +829,46 @@ class StateUpdate:
717
829
  addressable_area_name=addressable_area_name
718
830
  )
719
831
  return self
832
+
833
+ def update_flex_stacker_labware_pool_definition(
834
+ self,
835
+ module_id: str,
836
+ max_count: int,
837
+ primary_definition: LabwareDefinition,
838
+ adapter_definition: LabwareDefinition | None,
839
+ lid_definition: LabwareDefinition | None,
840
+ ) -> Self:
841
+ """Constrain the labware pool to a specific definition."""
842
+ self.flex_stacker_state_update = dataclasses.replace(
843
+ FlexStackerStateUpdate.create_or_override(
844
+ self.flex_stacker_state_update, module_id
845
+ ),
846
+ pool_constraint=FlexStackerPoolConstraint(
847
+ max_pool_count=max_count,
848
+ primary_definition=primary_definition,
849
+ lid_definition=lid_definition,
850
+ adapter_definition=adapter_definition,
851
+ ),
852
+ )
853
+ return self
854
+
855
+ def update_flex_stacker_labware_pool_count(
856
+ self, module_id: str, count: int
857
+ ) -> Self:
858
+ """Set the labware pool to a specific count."""
859
+ self.flex_stacker_state_update = dataclasses.replace(
860
+ FlexStackerStateUpdate.create_or_override(
861
+ self.flex_stacker_state_update, module_id
862
+ ),
863
+ pool_count=count,
864
+ )
865
+ return self
866
+
867
+ def set_pipette_ready_to_aspirate(
868
+ self, pipette_id: str, ready_to_aspirate: bool
869
+ ) -> Self:
870
+ """Set the ready to aspirate state for a pipette."""
871
+ self.ready_to_aspirate = PipetteAspirateReadyUpdate(
872
+ pipette_id=pipette_id, ready_to_aspirate=ready_to_aspirate
873
+ )
874
+ return self
@@ -1,7 +1,16 @@
1
1
  """Basic well data state and store."""
2
2
 
3
3
  from dataclasses import dataclass
4
- from typing import Dict, List, Union, Iterator, Optional, Tuple, overload, TypeVar
4
+ from typing import (
5
+ Dict,
6
+ List,
7
+ Union,
8
+ Iterator,
9
+ Optional,
10
+ Tuple,
11
+ overload,
12
+ )
13
+ from datetime import datetime
5
14
 
6
15
  from opentrons.protocol_engine.types import (
7
16
  ProbedHeightInfo,
@@ -10,6 +19,10 @@ from opentrons.protocol_engine.types import (
10
19
  WellInfoSummary,
11
20
  WellLiquidInfo,
12
21
  )
22
+ from opentrons.protocol_engine.types.liquid_level_detection import (
23
+ SimulatedProbeResult,
24
+ LiquidTrackingType,
25
+ )
13
26
 
14
27
  from . import update_types
15
28
  from ._abstract_store import HasState, HandlesActions
@@ -103,7 +116,7 @@ class WellStore(HasState[WellState], HandlesActions):
103
116
  del self._state.loaded_volumes[labware_id][well_name]
104
117
  else:
105
118
  prev_loaded_vol_info = self._state.loaded_volumes[labware_id][well_name]
106
- assert prev_loaded_vol_info.volume is not None
119
+ assert prev_loaded_vol_info.volume is not None, "volume info not loaded"
107
120
  self._state.loaded_volumes[labware_id][well_name] = LoadedVolumeInfo(
108
121
  volume=prev_loaded_vol_info.volume + volume_added,
109
122
  last_loaded=prev_loaded_vol_info.last_loaded,
@@ -119,13 +132,28 @@ class WellStore(HasState[WellState], HandlesActions):
119
132
  labware_id in self._state.probed_volumes
120
133
  and well_name in self._state.probed_volumes[labware_id]
121
134
  ):
135
+ prev_probed_vol_info = self._state.probed_volumes[labware_id][well_name]
122
136
  if volume_added is update_types.CLEAR:
123
137
  del self._state.probed_volumes[labware_id][well_name]
138
+ elif isinstance(
139
+ prev_probed_vol_info.volume,
140
+ SimulatedProbeResult,
141
+ ):
142
+ prev_probed_vol_info.volume.simulate_probed_aspirate_dispense(
143
+ volume_added
144
+ )
145
+ self._state.probed_volumes[labware_id][well_name] = ProbedVolumeInfo(
146
+ volume=prev_probed_vol_info.volume,
147
+ last_probed=prev_probed_vol_info.last_probed,
148
+ operations_since_probe=prev_probed_vol_info.operations_since_probe
149
+ + 1,
150
+ )
151
+ return
124
152
  else:
125
- prev_probed_vol_info = self._state.probed_volumes[labware_id][well_name]
126
153
  if prev_probed_vol_info.volume is None:
127
154
  new_vol_info: float | None = None
128
155
  else:
156
+ assert isinstance(prev_probed_vol_info.volume, float)
129
157
  new_vol_info = prev_probed_vol_info.volume + volume_added
130
158
  self._state.probed_volumes[labware_id][well_name] = ProbedVolumeInfo(
131
159
  volume=new_vol_info,
@@ -177,6 +205,22 @@ class WellView:
177
205
  probed_volume=probed_volume_info,
178
206
  )
179
207
 
208
+ def get_last_liquid_update(
209
+ self, labware_id: str, well_name: str
210
+ ) -> Optional[datetime]:
211
+ """Return the timestamp of the last load or probe done on the well."""
212
+ info = self.get_well_liquid_info(labware_id, well_name)
213
+ update_times: List[datetime] = []
214
+ if info.loaded_volume is not None and info.loaded_volume.volume is not None:
215
+ update_times.append(info.loaded_volume.last_loaded)
216
+ if info.probed_height is not None and info.probed_height.height is not None:
217
+ update_times.append(info.probed_height.last_probed)
218
+ if info.probed_volume is not None and info.probed_volume.volume is not None:
219
+ update_times.append(info.probed_volume.last_probed)
220
+ if len(update_times) > 0:
221
+ return max(update_times)
222
+ return None
223
+
180
224
  def get_all(self) -> List[WellInfoSummary]:
181
225
  """Get all well liquid info summaries."""
182
226
 
@@ -223,22 +267,24 @@ def _volume_from_info(info: Optional[LoadedVolumeInfo]) -> Optional[float]:
223
267
 
224
268
  def _volume_from_info(
225
269
  info: Union[ProbedVolumeInfo, LoadedVolumeInfo, None],
226
- ) -> Optional[float]:
270
+ ) -> Union[LiquidTrackingType, None]:
227
271
  if info is None:
228
272
  return None
229
273
  return info.volume
230
274
 
231
275
 
232
- def _height_from_info(info: Optional[ProbedHeightInfo]) -> Optional[float]:
276
+ def _height_from_info(
277
+ info: Optional[ProbedHeightInfo],
278
+ ) -> Union[LiquidTrackingType, None]:
233
279
  if info is None:
234
280
  return None
235
281
  return info.height
236
282
 
237
283
 
238
- MaybeClear = TypeVar("MaybeClear")
239
-
240
-
241
- def _none_from_clear(inval: MaybeClear | update_types.ClearType) -> MaybeClear | None:
284
+ def _none_from_clear(
285
+ inval: LiquidTrackingType | update_types.ClearType,
286
+ ) -> LiquidTrackingType | None:
242
287
  if inval == update_types.CLEAR:
243
288
  return None
289
+ assert isinstance(inval, (SimulatedProbeResult, float, int))
244
290
  return inval