opentrons 8.7.0a7__py3-none-any.whl → 8.8.0a7__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 (109) hide show
  1. opentrons/_version.py +2 -2
  2. opentrons/cli/analyze.py +4 -1
  3. opentrons/config/__init__.py +7 -0
  4. opentrons/drivers/asyncio/communication/serial_connection.py +8 -5
  5. opentrons/drivers/flex_stacker/driver.py +6 -1
  6. opentrons/drivers/vacuum_module/__init__.py +5 -0
  7. opentrons/drivers/vacuum_module/abstract.py +93 -0
  8. opentrons/drivers/vacuum_module/driver.py +208 -0
  9. opentrons/drivers/vacuum_module/errors.py +39 -0
  10. opentrons/drivers/vacuum_module/simulator.py +85 -0
  11. opentrons/drivers/vacuum_module/types.py +79 -0
  12. opentrons/execute.py +3 -0
  13. opentrons/hardware_control/backends/flex_protocol.py +2 -0
  14. opentrons/hardware_control/backends/ot3controller.py +35 -2
  15. opentrons/hardware_control/backends/ot3simulator.py +2 -0
  16. opentrons/hardware_control/backends/ot3utils.py +37 -0
  17. opentrons/hardware_control/module_control.py +23 -2
  18. opentrons/hardware_control/modules/mod_abc.py +1 -1
  19. opentrons/hardware_control/modules/types.py +1 -1
  20. opentrons/hardware_control/motion_utilities.py +6 -6
  21. opentrons/hardware_control/ot3api.py +62 -13
  22. opentrons/hardware_control/protocols/gripper_controller.py +1 -0
  23. opentrons/hardware_control/protocols/liquid_handler.py +6 -2
  24. opentrons/hardware_control/types.py +12 -0
  25. opentrons/legacy_commands/commands.py +58 -5
  26. opentrons/legacy_commands/module_commands.py +29 -0
  27. opentrons/legacy_commands/protocol_commands.py +33 -1
  28. opentrons/legacy_commands/types.py +75 -1
  29. opentrons/protocol_api/_transfer_liquid_validation.py +17 -2
  30. opentrons/protocol_api/_types.py +2 -0
  31. opentrons/protocol_api/core/engine/_default_labware_versions.py +1 -0
  32. opentrons/protocol_api/core/engine/deck_conflict.py +3 -1
  33. opentrons/protocol_api/core/engine/instrument.py +109 -26
  34. opentrons/protocol_api/core/engine/module_core.py +27 -3
  35. opentrons/protocol_api/core/engine/protocol.py +33 -1
  36. opentrons/protocol_api/core/engine/stringify.py +2 -0
  37. opentrons/protocol_api/core/instrument.py +19 -2
  38. opentrons/protocol_api/core/legacy/legacy_instrument_core.py +19 -2
  39. opentrons/protocol_api/core/legacy/legacy_module_core.py +15 -4
  40. opentrons/protocol_api/core/legacy/legacy_protocol_core.py +12 -0
  41. opentrons/protocol_api/core/legacy_simulator/legacy_instrument_core.py +19 -2
  42. opentrons/protocol_api/core/module.py +25 -2
  43. opentrons/protocol_api/core/protocol.py +12 -0
  44. opentrons/protocol_api/instrument_context.py +388 -2
  45. opentrons/protocol_api/labware.py +5 -2
  46. opentrons/protocol_api/module_contexts.py +133 -30
  47. opentrons/protocol_api/protocol_context.py +61 -17
  48. opentrons/protocol_api/robot_context.py +3 -4
  49. opentrons/protocol_api/validation.py +43 -2
  50. opentrons/protocol_engine/__init__.py +4 -0
  51. opentrons/protocol_engine/actions/__init__.py +2 -0
  52. opentrons/protocol_engine/actions/actions.py +9 -0
  53. opentrons/protocol_engine/commands/__init__.py +14 -0
  54. opentrons/protocol_engine/commands/absorbance_reader/read.py +22 -23
  55. opentrons/protocol_engine/commands/aspirate_while_tracking.py +52 -19
  56. opentrons/protocol_engine/commands/capture_image.py +302 -0
  57. opentrons/protocol_engine/commands/command.py +1 -0
  58. opentrons/protocol_engine/commands/command_unions.py +13 -0
  59. opentrons/protocol_engine/commands/dispense_while_tracking.py +56 -19
  60. opentrons/protocol_engine/commands/flex_stacker/common.py +35 -0
  61. opentrons/protocol_engine/commands/flex_stacker/set_stored_labware.py +7 -0
  62. opentrons/protocol_engine/commands/heater_shaker/set_shake_speed.py +1 -1
  63. opentrons/protocol_engine/commands/heater_shaker/set_target_temperature.py +1 -1
  64. opentrons/protocol_engine/commands/move_labware.py +3 -4
  65. opentrons/protocol_engine/commands/move_to_addressable_area_for_drop_tip.py +1 -1
  66. opentrons/protocol_engine/commands/movement_common.py +29 -2
  67. opentrons/protocol_engine/commands/pipetting_common.py +48 -3
  68. opentrons/protocol_engine/commands/thermocycler/set_target_block_temperature.py +12 -9
  69. opentrons/protocol_engine/commands/thermocycler/set_target_lid_temperature.py +17 -12
  70. opentrons/protocol_engine/commands/thermocycler/start_run_extended_profile.py +1 -1
  71. opentrons/protocol_engine/create_protocol_engine.py +12 -0
  72. opentrons/protocol_engine/engine_support.py +3 -0
  73. opentrons/protocol_engine/errors/__init__.py +8 -0
  74. opentrons/protocol_engine/errors/exceptions.py +64 -0
  75. opentrons/protocol_engine/execution/__init__.py +2 -0
  76. opentrons/protocol_engine/execution/command_executor.py +54 -1
  77. opentrons/protocol_engine/execution/create_queue_worker.py +4 -1
  78. opentrons/protocol_engine/execution/labware_movement.py +13 -4
  79. opentrons/protocol_engine/execution/pipetting.py +19 -25
  80. opentrons/protocol_engine/protocol_engine.py +62 -2
  81. opentrons/protocol_engine/resources/__init__.py +2 -0
  82. opentrons/protocol_engine/resources/camera_provider.py +110 -0
  83. opentrons/protocol_engine/resources/file_provider.py +133 -58
  84. opentrons/protocol_engine/slot_standardization.py +2 -0
  85. opentrons/protocol_engine/state/camera.py +54 -0
  86. opentrons/protocol_engine/state/commands.py +24 -4
  87. opentrons/protocol_engine/state/geometry.py +68 -10
  88. opentrons/protocol_engine/state/labware.py +10 -6
  89. opentrons/protocol_engine/state/labware_origin_math/stackup_origin_to_labware_origin.py +6 -1
  90. opentrons/protocol_engine/state/modules.py +9 -0
  91. opentrons/protocol_engine/state/preconditions.py +59 -0
  92. opentrons/protocol_engine/state/state.py +30 -0
  93. opentrons/protocol_engine/state/state_summary.py +2 -0
  94. opentrons/protocol_engine/state/update_types.py +10 -0
  95. opentrons/protocol_engine/types/__init__.py +14 -1
  96. opentrons/protocol_engine/types/command_preconditions.py +18 -0
  97. opentrons/protocol_engine/types/location.py +26 -2
  98. opentrons/protocol_engine/types/module.py +1 -1
  99. opentrons/protocol_runner/protocol_runner.py +14 -1
  100. opentrons/protocol_runner/run_orchestrator.py +31 -0
  101. opentrons/protocols/advanced_control/transfers/transfer_liquid_utils.py +2 -2
  102. opentrons/simulate.py +3 -0
  103. opentrons/system/camera.py +333 -3
  104. opentrons/system/ffmpeg.py +110 -0
  105. {opentrons-8.7.0a7.dist-info → opentrons-8.8.0a7.dist-info}/METADATA +4 -4
  106. {opentrons-8.7.0a7.dist-info → opentrons-8.8.0a7.dist-info}/RECORD +109 -97
  107. {opentrons-8.7.0a7.dist-info → opentrons-8.8.0a7.dist-info}/WHEEL +0 -0
  108. {opentrons-8.7.0a7.dist-info → opentrons-8.8.0a7.dist-info}/entry_points.txt +0 -0
  109. {opentrons-8.7.0a7.dist-info → opentrons-8.8.0a7.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,59 @@
1
+ """Command precondition state and store resource."""
2
+ from dataclasses import dataclass
3
+
4
+ from opentrons.protocol_engine.actions.get_state_update import get_state_updates
5
+ from opentrons.protocol_engine.state import update_types
6
+ from opentrons.protocol_engine.types import CommandPreconditions, PreconditionTypes
7
+
8
+ from ._abstract_store import HasState, HandlesActions
9
+ from ..actions import Action
10
+
11
+
12
+ @dataclass
13
+ class CommandPreconditionState:
14
+ """State of Engine command precondition references."""
15
+
16
+ preconditions: CommandPreconditions
17
+
18
+
19
+ class CommandPreconditionStore(HasState[CommandPreconditionState], HandlesActions):
20
+ """Command Precondition container."""
21
+
22
+ _state: CommandPreconditionState
23
+
24
+ def __init__(self) -> None:
25
+ """Initialize a Command Precondition store and its state."""
26
+ self._state = CommandPreconditionState(
27
+ preconditions=CommandPreconditions(isCameraUsed=False)
28
+ )
29
+
30
+ def handle_action(self, action: Action) -> None:
31
+ """Modify state in reaction to an action."""
32
+ for state_update in get_state_updates(action):
33
+ self._handle_state_update(state_update)
34
+
35
+ def _handle_state_update(self, state_update: update_types.StateUpdate) -> None:
36
+ if state_update.precondition_update != update_types.NO_CHANGE:
37
+ for key in state_update.precondition_update.preconditions:
38
+ if key == PreconditionTypes.IS_CAMERA_USED:
39
+ self._state.preconditions.isCameraUsed = (
40
+ state_update.precondition_update.preconditions[key]
41
+ )
42
+
43
+
44
+ class CommandPreconditionView:
45
+ """Read-only engine created Command Precondition state view."""
46
+
47
+ _state: CommandPreconditionState
48
+
49
+ def __init__(self, state: CommandPreconditionState) -> None:
50
+ """Initialize the view of Command Precondition state.
51
+
52
+ Arguments:
53
+ state: Command precondition dataclass used for tracking preconditions used during a protocol.
54
+ """
55
+ self._state = state
56
+
57
+ def get_precondition(self) -> CommandPreconditions:
58
+ """Get the Command Preconditions currently set by a protocol."""
59
+ return self._state.preconditions
@@ -35,6 +35,12 @@ from .config import Config
35
35
  from .state_summary import StateSummary
36
36
  from ..types import DeckConfigurationType
37
37
  from .tasks import TaskState, TaskView, TaskStore
38
+ from .preconditions import (
39
+ CommandPreconditionState,
40
+ CommandPreconditionStore,
41
+ CommandPreconditionView,
42
+ )
43
+ from .camera import CameraState, CameraView, CameraStore
38
44
 
39
45
 
40
46
  _ParamsT = ParamSpec("_ParamsT")
@@ -56,6 +62,8 @@ class State:
56
62
  wells: WellState
57
63
  files: FileState
58
64
  tasks: TaskState
65
+ preconditions: CommandPreconditionState
66
+ camera: CameraState
59
67
 
60
68
 
61
69
  class StateView(HasState[State]):
@@ -76,6 +84,8 @@ class StateView(HasState[State]):
76
84
  _files: FileView
77
85
  _config: Config
78
86
  _tasks: TaskView
87
+ _preconditions: CommandPreconditionView
88
+ _camera: CameraView
79
89
 
80
90
  @property
81
91
  def commands(self) -> CommandView:
@@ -142,6 +152,16 @@ class StateView(HasState[State]):
142
152
  """Get ProtocolEngine configuration."""
143
153
  return self._config
144
154
 
155
+ @property
156
+ def preconditions(self) -> CommandPreconditionView:
157
+ """Get state view selectors for command preconditions."""
158
+ return self._preconditions
159
+
160
+ @property
161
+ def camera(self) -> CameraView:
162
+ """Get state view for the Camera."""
163
+ return self._camera
164
+
145
165
  @property
146
166
  def tasks(self) -> TaskView:
147
167
  """Get state view selectors for task state."""
@@ -171,6 +191,7 @@ class StateView(HasState[State]):
171
191
  for liquid_class_id, liquid_class_record in self._liquid_classes.get_all().items()
172
192
  ],
173
193
  tasks=self._tasks.get_summary(),
194
+ cameraSettings=self._camera.get_enablement_settings(),
174
195
  )
175
196
 
176
197
 
@@ -241,6 +262,8 @@ class StateStore(StateView, ActionHandler):
241
262
  self._well_store = WellStore()
242
263
  self._file_store = FileStore()
243
264
  self._task_store = TaskStore()
265
+ self._precondition_store = CommandPreconditionStore()
266
+ self._camera_store = CameraStore()
244
267
 
245
268
  self._substores: List[HandlesActions] = [
246
269
  self._command_store,
@@ -254,6 +277,8 @@ class StateStore(StateView, ActionHandler):
254
277
  self._well_store,
255
278
  self._file_store,
256
279
  self._task_store,
280
+ self._precondition_store,
281
+ self._camera_store,
257
282
  ]
258
283
  self._config = config
259
284
  self._change_notifier = change_notifier or ChangeNotifier()
@@ -378,6 +403,8 @@ class StateStore(StateView, ActionHandler):
378
403
  wells=self._well_store.state,
379
404
  files=self._file_store.state,
380
405
  tasks=self._task_store.state,
406
+ preconditions=self._precondition_store.state,
407
+ camera=self._camera_store.state,
381
408
  )
382
409
 
383
410
  def _initialize_state(self) -> None:
@@ -397,6 +424,8 @@ class StateStore(StateView, ActionHandler):
397
424
  self._wells = WellView(state.wells)
398
425
  self._files = FileView(state.files)
399
426
  self._tasks = TaskView(state.tasks)
427
+ self._preconditions = CommandPreconditionView(state.preconditions)
428
+ self._camera = CameraView(state.camera)
400
429
 
401
430
  # Derived states
402
431
  self._geometry = GeometryView(
@@ -430,6 +459,7 @@ class StateStore(StateView, ActionHandler):
430
459
  self._tips._state = next_state.tips
431
460
  self._wells._state = next_state.wells
432
461
  self._tasks._state = next_state.tasks
462
+ self._camera._state = next_state.camera
433
463
  self._change_notifier.notify()
434
464
  if self._notify_robot_server is not None:
435
465
  self._notify_robot_server()
@@ -15,6 +15,7 @@ from ..types import (
15
15
  WellInfoSummary,
16
16
  TaskSummary,
17
17
  )
18
+ from ..resources.camera_provider import CameraSettings
18
19
 
19
20
 
20
21
  class StateSummary(BaseModel):
@@ -36,3 +37,4 @@ class StateSummary(BaseModel):
36
37
  files: List[str] = Field(default_factory=list)
37
38
  liquidClasses: List[LiquidClassRecordWithId] = Field(default_factory=list)
38
39
  tasks: List[TaskSummary] = Field(default_factory=list)
40
+ cameraSettings: Optional[CameraSettings] = None
@@ -24,6 +24,7 @@ from opentrons.protocol_engine.types import (
24
24
  ModuleModel,
25
25
  ModuleDefinition,
26
26
  LabwareWellId,
27
+ PreconditionTypes,
27
28
  )
28
29
  from opentrons.types import MountType, DeckSlotName
29
30
  from opentrons_shared_data.labware.labware_definition import LabwareDefinition
@@ -401,6 +402,13 @@ class FilesAddedUpdate:
401
402
  file_ids: list[str]
402
403
 
403
404
 
405
+ @dataclasses.dataclass
406
+ class PreconditionUpdate:
407
+ """An update that changes command preconditions flags."""
408
+
409
+ preconditions: dict[PreconditionTypes, bool]
410
+
411
+
404
412
  @dataclasses.dataclass
405
413
  class AddressableAreaUsedUpdate:
406
414
  """An update that says an addressable area has been used."""
@@ -477,6 +485,8 @@ class StateUpdate:
477
485
 
478
486
  ready_to_aspirate: PipetteAspirateReadyUpdate | NoChangeType = NO_CHANGE
479
487
 
488
+ precondition_update: PreconditionUpdate | NoChangeType = NO_CHANGE
489
+
480
490
  def append(self, other: Self) -> Self:
481
491
  """Apply another `StateUpdate` "on top of" this one.
482
492
 
@@ -25,6 +25,10 @@ from .command_annotations import (
25
25
  CustomCommandAnnotation,
26
26
  CommandAnnotation,
27
27
  )
28
+ from .command_preconditions import (
29
+ CommandPreconditions,
30
+ PreconditionTypes,
31
+ )
28
32
  from .partial_tip_configuration import (
29
33
  AllNozzleLayoutConfiguration,
30
34
  SingleNozzleLayoutConfiguration,
@@ -83,6 +87,8 @@ from .location import (
83
87
  NonStackedLocation,
84
88
  DeckPoint,
85
89
  InStackerHopperLocation,
90
+ WASTE_CHUTE_LOCATION,
91
+ AccessibleByGripperLocation,
86
92
  OnLabwareLocationSequenceComponent,
87
93
  OnModuleLocationSequenceComponent,
88
94
  OnAddressableAreaLocationSequenceComponent,
@@ -92,6 +98,7 @@ from .location import (
92
98
  LoadableLabwareLocation,
93
99
  labware_location_is_system,
94
100
  labware_location_is_off_deck,
101
+ labware_location_is_in_waste_chute,
95
102
  )
96
103
  from .labware import (
97
104
  OverlapOffset,
@@ -166,6 +173,9 @@ __all__ = [
166
173
  "SecondOrderCommandAnnotation",
167
174
  "CustomCommandAnnotation",
168
175
  "CommandAnnotation",
176
+ # Command preconditions
177
+ "PreconditionTypes",
178
+ "CommandPreconditions",
169
179
  # Partial tip handling
170
180
  "AllNozzleLayoutConfiguration",
171
181
  "SingleNozzleLayoutConfiguration",
@@ -229,8 +239,10 @@ __all__ = [
229
239
  "NonStackedLocation",
230
240
  "DeckPoint",
231
241
  "OffDeckLocationType",
232
- "SystemLocationType",
242
+ "WasteChuteLocationType" "SystemLocationType",
233
243
  "InStackerHopperLocation",
244
+ "WASTE_CHUTE_LOCATION",
245
+ "AccessibleByGripperLocation",
234
246
  "OnLabwareLocationSequenceComponent",
235
247
  "OnModuleLocationSequenceComponent",
236
248
  "OnAddressableAreaLocationSequenceComponent",
@@ -240,6 +252,7 @@ __all__ = [
240
252
  "LoadableLabwareLocation",
241
253
  "labware_location_is_off_deck",
242
254
  "labware_location_is_system",
255
+ "labware_location_is_in_waste_chute",
243
256
  # Labware offset location
244
257
  "LegacyLabwareOffsetLocation",
245
258
  "LabwareOffsetLocationSequence",
@@ -0,0 +1,18 @@
1
+ """Protocol Engine types dealing with command preconditions."""
2
+ from enum import Enum
3
+ from pydantic import Field, BaseModel
4
+
5
+
6
+ class PreconditionTypes(str, Enum):
7
+ """Precondition types used for identification during state update."""
8
+
9
+ IS_CAMERA_USED = "isCameraUsed"
10
+
11
+
12
+ class CommandPreconditions(BaseModel):
13
+ """Preconditions of commands as described in protocol analysis."""
14
+
15
+ isCameraUsed: bool = Field(
16
+ default=False,
17
+ description="Parameter to determine if a Camera is used in a protocol.",
18
+ )
@@ -85,8 +85,10 @@ class InStackerHopperLocation(BaseModel):
85
85
 
86
86
  _OffDeckLocationType = Literal["offDeck"]
87
87
  _SystemLocationType = Literal["systemLocation"]
88
+ _WasteChuteLocationType = Literal["wasteChuteLocation"]
88
89
  OFF_DECK_LOCATION: _OffDeckLocationType = "offDeck"
89
90
  SYSTEM_LOCATION: _SystemLocationType = "systemLocation"
91
+ WASTE_CHUTE_LOCATION: _WasteChuteLocationType = "wasteChuteLocation"
90
92
 
91
93
 
92
94
  def labware_location_is_off_deck(
@@ -103,6 +105,13 @@ def labware_location_is_system(
103
105
  return isinstance(location, str) and location == SYSTEM_LOCATION
104
106
 
105
107
 
108
+ def labware_location_is_in_waste_chute(
109
+ location: LabwareLocation,
110
+ ) -> TypeGuard[_WasteChuteLocationType]:
111
+ """Check if a location is the waste chute."""
112
+ return isinstance(location, str) and location == WASTE_CHUTE_LOCATION
113
+
114
+
106
115
  class OnLabwareLocationSequenceComponent(BaseModel):
107
116
  """Labware on another labware."""
108
117
 
@@ -137,7 +146,7 @@ class NotOnDeckLocationSequenceComponent(BaseModel):
137
146
  """Labware on a system location."""
138
147
 
139
148
  kind: Literal["notOnDeck"] = "notOnDeck"
140
- logicalLocationName: _OffDeckLocationType | _SystemLocationType
149
+ logicalLocationName: _OffDeckLocationType | _SystemLocationType | _WasteChuteLocationType
141
150
 
142
151
 
143
152
  LabwareLocationSequence = list[
@@ -158,6 +167,7 @@ LabwareLocation = Union[
158
167
  _SystemLocationType,
159
168
  AddressableAreaLocation,
160
169
  InStackerHopperLocation,
170
+ _WasteChuteLocationType,
161
171
  ]
162
172
  """Union of all locations where it's legal to keep a labware."""
163
173
 
@@ -168,11 +178,15 @@ LoadableLabwareLocation = Union[
168
178
  _OffDeckLocationType,
169
179
  _SystemLocationType,
170
180
  AddressableAreaLocation,
181
+ _WasteChuteLocationType,
171
182
  ]
172
183
  """Union of all locations where it's legal to load a labware."""
173
184
 
174
185
  OnDeckLabwareLocation = Union[
175
- DeckSlotLocation, ModuleLocation, OnLabwareLocation, AddressableAreaLocation
186
+ DeckSlotLocation,
187
+ ModuleLocation,
188
+ OnLabwareLocation,
189
+ AddressableAreaLocation,
176
190
  ]
177
191
 
178
192
  NonStackedLocation = Union[
@@ -181,9 +195,19 @@ NonStackedLocation = Union[
181
195
  ModuleLocation,
182
196
  _OffDeckLocationType,
183
197
  _SystemLocationType,
198
+ _WasteChuteLocationType,
184
199
  ]
185
200
  """Union of all locations where it's legal to keep a labware that can't be stacked on another labware"""
186
201
 
202
+ AccessibleByGripperLocation = Union[
203
+ DeckSlotLocation,
204
+ ModuleLocation,
205
+ OnLabwareLocation,
206
+ AddressableAreaLocation,
207
+ _WasteChuteLocationType,
208
+ ]
209
+ """Union of all locations that a gripper can move things to."""
210
+
187
211
 
188
212
  # TODO(mm, 2022-11-07): Deduplicate with Vec3f.
189
213
  class DeckPoint(BaseModel):
@@ -303,7 +303,7 @@ class StackerStoredLabwareGroup(BaseModel):
303
303
 
304
304
  @dataclass
305
305
  class StackerPoolDefinition:
306
- """Represents an internal configuraiton of stored labware."""
306
+ """Represents an internal configuration of stored labware."""
307
307
 
308
308
  primaryLabwareDefinition: LabwareDefinition
309
309
  adapterLabwareDefinition: LabwareDefinition | SkipJsonSchema[None] = None
@@ -24,7 +24,7 @@ from opentrons.protocol_engine import (
24
24
  Command,
25
25
  commands as pe_commands,
26
26
  )
27
- from opentrons.protocol_engine.types import CommandAnnotation
27
+ from opentrons.protocol_engine.types import CommandAnnotation, CommandPreconditions
28
28
  from opentrons.protocols.parse import PythonParseMode
29
29
  from opentrons.util.async_helpers import asyncio_yield
30
30
  from opentrons.util.broker import Broker
@@ -58,6 +58,7 @@ class RunResult(NamedTuple):
58
58
  state_summary: StateSummary
59
59
  parameters: List[RunTimeParameter]
60
60
  command_annotations: List[CommandAnnotation]
61
+ command_preconditions: Optional[CommandPreconditions]
61
62
 
62
63
 
63
64
  class AbstractRunner(ABC):
@@ -284,11 +285,15 @@ class PythonAndLegacyRunner(AbstractRunner):
284
285
  run_data = self._protocol_engine.state_view.get_summary()
285
286
  commands = self._protocol_engine.state_view.commands.get_all()
286
287
  parameters = self.run_time_parameters
288
+ preconditions = (
289
+ self._protocol_engine.state_view.preconditions.get_precondition()
290
+ )
287
291
  return RunResult(
288
292
  commands=commands,
289
293
  state_summary=run_data,
290
294
  parameters=parameters,
291
295
  command_annotations=[],
296
+ command_preconditions=preconditions,
292
297
  )
293
298
 
294
299
 
@@ -403,11 +408,15 @@ class JsonRunner(AbstractRunner):
403
408
 
404
409
  run_data = self._protocol_engine.state_view.get_summary()
405
410
  commands = self._protocol_engine.state_view.commands.get_all()
411
+ preconditions = (
412
+ self._protocol_engine.state_view.preconditions.get_precondition()
413
+ )
406
414
  return RunResult(
407
415
  commands=commands,
408
416
  state_summary=run_data,
409
417
  parameters=[],
410
418
  command_annotations=self._command_annotations,
419
+ command_preconditions=preconditions,
411
420
  )
412
421
 
413
422
  async def _add_and_execute_commands(self) -> None:
@@ -479,11 +488,15 @@ class LiveRunner(AbstractRunner):
479
488
 
480
489
  run_data = self._protocol_engine.state_view.get_summary()
481
490
  commands = self._protocol_engine.state_view.commands.get_all()
491
+ preconditions = (
492
+ self._protocol_engine.state_view.preconditions.get_precondition()
493
+ )
482
494
  return RunResult(
483
495
  commands=commands,
484
496
  state_summary=run_data,
485
497
  parameters=[],
486
498
  command_annotations=[],
499
+ command_preconditions=preconditions,
487
500
  )
488
501
 
489
502
 
@@ -43,7 +43,9 @@ from ..protocol_engine.types import (
43
43
  CSVRuntimeParamPaths,
44
44
  CommandAnnotation,
45
45
  ModuleModel,
46
+ CommandPreconditions,
46
47
  )
48
+ from ..protocol_engine.resources.camera_provider import CameraProvider, CameraSettings
47
49
  from ..protocol_engine.error_recovery_policy import ErrorRecoveryPolicy
48
50
 
49
51
  from ..protocol_reader import JsonProtocolConfig, PythonProtocolConfig, ProtocolSource
@@ -84,6 +86,7 @@ class RunOrchestrator:
84
86
  _protocol_live_runner: protocol_runner.LiveRunner
85
87
  _hardware_api: HardwareControlAPI
86
88
  _protocol_engine: ProtocolEngine
89
+ _camera_provider: Optional[CameraProvider] = None
87
90
 
88
91
  def __init__(
89
92
  self,
@@ -92,6 +95,7 @@ class RunOrchestrator:
92
95
  fixit_runner: protocol_runner.LiveRunner,
93
96
  setup_runner: protocol_runner.LiveRunner,
94
97
  protocol_live_runner: protocol_runner.LiveRunner,
98
+ camera_provider: Optional[CameraProvider] = None,
95
99
  json_or_python_protocol_runner: Optional[
96
100
  Union[protocol_runner.PythonAndLegacyRunner, protocol_runner.JsonRunner]
97
101
  ] = None,
@@ -106,6 +110,7 @@ class RunOrchestrator:
106
110
  setup_runner: LiveRunner for setup commands.
107
111
  protocol_live_runner: LiveRunner for protocol commands.
108
112
  json_or_python_protocol_runner: JsonRunner/PythonAndLegacyRunner for protocol commands.
113
+ camera_provider: Provides callbacks to Camera interface.
109
114
  run_id: run id if any, associated to the runner/engine.
110
115
  """
111
116
  self._run_id = run_id
@@ -114,6 +119,7 @@ class RunOrchestrator:
114
119
  self._setup_runner = setup_runner
115
120
  self._fixit_runner = fixit_runner
116
121
  self._protocol_live_runner = protocol_live_runner
122
+ self._camera_provider = camera_provider
117
123
  self._fixit_runner.prepare()
118
124
  self._setup_runner.prepare()
119
125
  self._protocol_engine.set_and_start_queue_worker(self.command_generator)
@@ -132,6 +138,7 @@ class RunOrchestrator:
132
138
  cls,
133
139
  hardware_api: HardwareControlAPI,
134
140
  protocol_engine: ProtocolEngine,
141
+ camera_provider: Optional[CameraProvider] = None,
135
142
  protocol_config: Optional[
136
143
  Union[JsonProtocolConfig, PythonProtocolConfig]
137
144
  ] = None,
@@ -169,6 +176,7 @@ class RunOrchestrator:
169
176
  hardware_api=hardware_api,
170
177
  protocol_engine=protocol_engine,
171
178
  protocol_live_runner=protocol_live_runner,
179
+ camera_provider=camera_provider,
172
180
  )
173
181
 
174
182
  def play(self, deck_configuration: Optional[DeckConfigurationType] = None) -> None:
@@ -237,6 +245,10 @@ class RunOrchestrator:
237
245
  """Get protocol run data."""
238
246
  return self._protocol_engine.state_view.get_summary()
239
247
 
248
+ def get_preconditions(self) -> CommandPreconditions:
249
+ """Get the preconditions of a protocol run."""
250
+ return self._protocol_engine.state_view.preconditions.get_precondition()
251
+
240
252
  def get_loaded_labware_definitions(self) -> List[LabwareDefinition]:
241
253
  """Get loaded labware definitions."""
242
254
  return self._protocol_engine.state_view.labware.get_loaded_labware_definitions()
@@ -363,6 +375,13 @@ class RunOrchestrator:
363
375
  """Add a new labware definition to state."""
364
376
  return self._protocol_engine.add_labware_definition(definition)
365
377
 
378
+ def add_camera_enablement_settings(
379
+ self,
380
+ enablement_settings: CameraSettings,
381
+ ) -> CameraSettings:
382
+ """Add new camera enablement settings."""
383
+ return self._protocol_engine.add_camera_enablement_settings(enablement_settings)
384
+
366
385
  async def add_command_and_wait_for_interval(
367
386
  self,
368
387
  command: CommandCreate,
@@ -396,6 +415,18 @@ class RunOrchestrator:
396
415
  module_model=ModuleModel.from_hardware(module_model), serial=module_serial
397
416
  )
398
417
 
418
+ async def module_disconnected(
419
+ self, module_model: HardwareModuleModel, module_serial: str | None
420
+ ) -> bool:
421
+ """Handle an unexpected module disconnection.
422
+
423
+ If this function returns true, the caller should call finish() immediately; if it returns
424
+ False, the caller should not call finish() until it otherwise would.
425
+ """
426
+ return await self._protocol_engine.module_disconnected(
427
+ module_model=ModuleModel.from_hardware(module_model), serial=module_serial
428
+ )
429
+
399
430
  async def use_attached_modules(
400
431
  self, modules_by_id: Dict[str, HardwareModuleAPI]
401
432
  ) -> None:
@@ -72,7 +72,7 @@ def raise_if_location_inside_liquid(
72
72
  def group_wells_for_multi_channel_transfer(
73
73
  targets: Sequence[Well],
74
74
  nozzle_map: NozzleMapInterface,
75
- target_name: Literal["source", "destination"],
75
+ target_name: Literal["source", "destination", "tip"],
76
76
  ) -> List[Well]:
77
77
  """Takes a list of wells and a nozzle map and returns a list of target wells to address every well given
78
78
 
@@ -108,7 +108,7 @@ def group_wells_for_multi_channel_transfer(
108
108
  def _group_wells_for_nozzle_configuration( # noqa: C901
109
109
  targets: List[Well],
110
110
  nozzle_map: NozzleMapInterface,
111
- target_name: Literal["source", "destination"],
111
+ target_name: Literal["source", "destination", "tip"],
112
112
  ) -> List[Well]:
113
113
  """Groups wells together for a column, row, or full 96 configuration and returns a reduced list of target wells."""
114
114
  grouped_wells = []
opentrons/simulate.py CHANGED
@@ -50,6 +50,7 @@ from opentrons.protocol_engine.create_protocol_engine import (
50
50
  from opentrons.protocol_engine import error_recovery_policy
51
51
  from opentrons.protocol_engine.state.config import Config
52
52
  from opentrons.protocol_engine.types import DeckType, EngineStatus, PostRunHardwareState
53
+ from opentrons.protocol_engine.resources.camera_provider import CameraProvider
53
54
  from opentrons.protocol_reader.protocol_source import ProtocolSource
54
55
  from opentrons.protocol_runner.protocol_runner import create_protocol_runner, LiveRunner
55
56
  from opentrons.protocol_runner import RunOrchestrator
@@ -849,6 +850,7 @@ def _create_live_context_pe(
849
850
  load_fixed_trash=should_load_fixed_trash_labware_for_python_protocol(
850
851
  api_version
851
852
  ),
853
+ camera_provider=CameraProvider(),
852
854
  ) as (engine, loop):
853
855
  yield engine, loop
854
856
  finally:
@@ -982,6 +984,7 @@ def _run_file_pe(
982
984
  protocol_live_runner=LiveRunner(
983
985
  protocol_engine=protocol_engine, hardware_api=hardware_api_wrapped
984
986
  ),
987
+ camera_provider=CameraProvider(),
985
988
  )
986
989
 
987
990
  # TODO(mm, 2024-08-06): This home is theoretically redundant with Protocol