opentrons 8.7.0a1__py3-none-any.whl → 8.7.0a2__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.
- opentrons/_version.py +2 -2
- opentrons/drivers/thermocycler/abstract.py +1 -0
- opentrons/drivers/thermocycler/driver.py +33 -4
- opentrons/drivers/thermocycler/simulator.py +2 -0
- opentrons/hardware_control/api.py +24 -5
- opentrons/hardware_control/backends/controller.py +8 -2
- opentrons/hardware_control/backends/ot3controller.py +3 -0
- opentrons/hardware_control/backends/ot3simulator.py +2 -1
- opentrons/hardware_control/backends/simulator.py +2 -1
- opentrons/hardware_control/backends/subsystem_manager.py +5 -2
- opentrons/hardware_control/module_control.py +82 -8
- opentrons/hardware_control/modules/__init__.py +3 -0
- opentrons/hardware_control/modules/absorbance_reader.py +11 -4
- opentrons/hardware_control/modules/flex_stacker.py +38 -9
- opentrons/hardware_control/modules/heater_shaker.py +30 -5
- opentrons/hardware_control/modules/magdeck.py +8 -4
- opentrons/hardware_control/modules/mod_abc.py +13 -5
- opentrons/hardware_control/modules/tempdeck.py +25 -5
- opentrons/hardware_control/modules/thermocycler.py +56 -10
- opentrons/hardware_control/modules/types.py +20 -1
- opentrons/hardware_control/modules/utils.py +11 -4
- opentrons/hardware_control/nozzle_manager.py +3 -0
- opentrons/hardware_control/ot3api.py +26 -5
- opentrons/hardware_control/scripts/update_module_fw.py +5 -0
- opentrons/hardware_control/types.py +31 -2
- opentrons/legacy_commands/protocol_commands.py +20 -0
- opentrons/legacy_commands/types.py +42 -0
- opentrons/motion_planning/waypoints.py +15 -29
- opentrons/protocol_api/__init__.py +5 -0
- opentrons/protocol_api/_types.py +6 -1
- opentrons/protocol_api/core/common.py +3 -1
- opentrons/protocol_api/core/engine/_default_labware_versions.py +32 -11
- opentrons/protocol_api/core/engine/labware.py +8 -1
- opentrons/protocol_api/core/engine/module_core.py +4 -0
- opentrons/protocol_api/core/engine/protocol.py +18 -1
- opentrons/protocol_api/core/engine/tasks.py +35 -0
- opentrons/protocol_api/core/legacy/legacy_module_core.py +2 -0
- opentrons/protocol_api/core/legacy/legacy_protocol_core.py +11 -1
- opentrons/protocol_api/core/legacy/tasks.py +19 -0
- opentrons/protocol_api/core/legacy_simulator/legacy_protocol_core.py +14 -2
- opentrons/protocol_api/core/legacy_simulator/tasks.py +19 -0
- opentrons/protocol_api/core/module.py +1 -0
- opentrons/protocol_api/core/protocol.py +11 -2
- opentrons/protocol_api/core/tasks.py +31 -0
- opentrons/protocol_api/module_contexts.py +1 -0
- opentrons/protocol_api/protocol_context.py +26 -4
- opentrons/protocol_api/robot_context.py +38 -21
- opentrons/protocol_api/tasks.py +48 -0
- opentrons/protocol_api/validation.py +6 -1
- opentrons/protocol_engine/actions/__init__.py +4 -2
- opentrons/protocol_engine/actions/actions.py +22 -9
- opentrons/protocol_engine/clients/sync_client.py +6 -7
- opentrons/protocol_engine/commands/__init__.py +42 -0
- opentrons/protocol_engine/commands/absorbance_reader/close_lid.py +2 -15
- opentrons/protocol_engine/commands/absorbance_reader/open_lid.py +2 -15
- opentrons/protocol_engine/commands/aspirate.py +1 -0
- opentrons/protocol_engine/commands/command.py +1 -0
- opentrons/protocol_engine/commands/command_unions.py +39 -0
- opentrons/protocol_engine/commands/create_timer.py +83 -0
- opentrons/protocol_engine/commands/dispense.py +1 -0
- opentrons/protocol_engine/commands/drop_tip.py +32 -8
- opentrons/protocol_engine/commands/movement_common.py +2 -0
- opentrons/protocol_engine/commands/pick_up_tip.py +21 -11
- opentrons/protocol_engine/commands/set_tip_state.py +97 -0
- opentrons/protocol_engine/commands/thermocycler/run_extended_profile.py +6 -0
- opentrons/protocol_engine/commands/thermocycler/run_profile.py +8 -0
- opentrons/protocol_engine/commands/thermocycler/set_target_block_temperature.py +17 -1
- opentrons/protocol_engine/commands/touch_tip.py +1 -1
- opentrons/protocol_engine/commands/unsafe/unsafe_place_labware.py +6 -22
- opentrons/protocol_engine/commands/wait_for_tasks.py +98 -0
- opentrons/protocol_engine/errors/__init__.py +4 -0
- opentrons/protocol_engine/errors/exceptions.py +55 -0
- opentrons/protocol_engine/execution/__init__.py +2 -0
- opentrons/protocol_engine/execution/command_executor.py +8 -0
- opentrons/protocol_engine/execution/create_queue_worker.py +5 -1
- opentrons/protocol_engine/execution/labware_movement.py +9 -12
- opentrons/protocol_engine/execution/movement.py +2 -0
- opentrons/protocol_engine/execution/queue_worker.py +4 -0
- opentrons/protocol_engine/execution/run_control.py +8 -0
- opentrons/protocol_engine/execution/task_handler.py +157 -0
- opentrons/protocol_engine/protocol_engine.py +67 -33
- opentrons/protocol_engine/resources/__init__.py +2 -0
- opentrons/protocol_engine/resources/concurrency_provider.py +27 -0
- opentrons/protocol_engine/resources/deck_configuration_provider.py +7 -0
- opentrons/protocol_engine/resources/labware_validation.py +10 -6
- opentrons/protocol_engine/state/_well_math.py +60 -18
- opentrons/protocol_engine/state/addressable_areas.py +2 -0
- opentrons/protocol_engine/state/commands.py +7 -7
- opentrons/protocol_engine/state/geometry.py +204 -374
- opentrons/protocol_engine/state/labware.py +52 -102
- opentrons/protocol_engine/state/labware_origin_math/errors.py +94 -0
- opentrons/protocol_engine/state/labware_origin_math/stackup_origin_to_labware_origin.py +1331 -0
- opentrons/protocol_engine/state/module_substates/thermocycler_module_substate.py +37 -0
- opentrons/protocol_engine/state/modules.py +21 -8
- opentrons/protocol_engine/state/motion.py +44 -0
- opentrons/protocol_engine/state/state.py +14 -0
- opentrons/protocol_engine/state/state_summary.py +2 -0
- opentrons/protocol_engine/state/tasks.py +139 -0
- opentrons/protocol_engine/state/tips.py +177 -258
- opentrons/protocol_engine/state/update_types.py +16 -9
- opentrons/protocol_engine/types/__init__.py +9 -3
- opentrons/protocol_engine/types/deck_configuration.py +5 -1
- opentrons/protocol_engine/types/instrument.py +8 -1
- opentrons/protocol_engine/types/labware.py +1 -13
- opentrons/protocol_engine/types/module.py +10 -0
- opentrons/protocol_engine/types/tasks.py +38 -0
- opentrons/protocol_engine/types/tip.py +9 -0
- opentrons/protocol_runner/create_simulating_orchestrator.py +29 -2
- opentrons/protocol_runner/run_orchestrator.py +18 -2
- opentrons/protocols/api_support/definitions.py +1 -1
- opentrons/protocols/api_support/types.py +2 -1
- opentrons/simulate.py +48 -15
- opentrons/system/camera.py +1 -1
- {opentrons-8.7.0a1.dist-info → opentrons-8.7.0a2.dist-info}/METADATA +4 -4
- {opentrons-8.7.0a1.dist-info → opentrons-8.7.0a2.dist-info}/RECORD +118 -105
- opentrons/protocol_engine/state/_labware_origin_math.py +0 -636
- {opentrons-8.7.0a1.dist-info → opentrons-8.7.0a2.dist-info}/WHEEL +0 -0
- {opentrons-8.7.0a1.dist-info → opentrons-8.7.0a2.dist-info}/entry_points.txt +0 -0
- {opentrons-8.7.0a1.dist-info → opentrons-8.7.0a2.dist-info}/licenses/LICENSE +0 -0
|
@@ -11,6 +11,7 @@ from .labware_data_provider import LabwareDataProvider
|
|
|
11
11
|
from .module_data_provider import ModuleDataProvider
|
|
12
12
|
from .file_provider import FileProvider
|
|
13
13
|
from .ot3_validation import ensure_ot3_hardware
|
|
14
|
+
from .concurrency_provider import ConcurrencyProvider
|
|
14
15
|
|
|
15
16
|
|
|
16
17
|
__all__ = [
|
|
@@ -18,6 +19,7 @@ __all__ = [
|
|
|
18
19
|
"LabwareDataProvider",
|
|
19
20
|
"DeckDataProvider",
|
|
20
21
|
"DeckFixedLabware",
|
|
22
|
+
"ConcurrencyProvider",
|
|
21
23
|
"ModuleDataProvider",
|
|
22
24
|
"FileProvider",
|
|
23
25
|
"ensure_ot3_hardware",
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"""Concurrency primitives providers."""
|
|
2
|
+
import asyncio
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class ConcurrencyProvider:
|
|
6
|
+
"""Concurrency primitives for engine tasks."""
|
|
7
|
+
|
|
8
|
+
def __init__(self) -> None:
|
|
9
|
+
"""Build a concurrency provider."""
|
|
10
|
+
self._locks: dict[str, asyncio.Lock] = {}
|
|
11
|
+
self._queues: dict[str, "asyncio.Queue[asyncio.Task[None]]"] = {}
|
|
12
|
+
|
|
13
|
+
def lock_for_group(self, group_id: str) -> asyncio.Lock:
|
|
14
|
+
"""Returns the lock for specified group id."""
|
|
15
|
+
try:
|
|
16
|
+
return self._locks[group_id]
|
|
17
|
+
except KeyError:
|
|
18
|
+
self._locks[group_id] = asyncio.Lock()
|
|
19
|
+
return self._locks[group_id]
|
|
20
|
+
|
|
21
|
+
def queue_for_group(self, group_id: str) -> "asyncio.Queue[asyncio.Task[None]]":
|
|
22
|
+
"""Returns the queue for specified group id."""
|
|
23
|
+
try:
|
|
24
|
+
return self._queues[group_id]
|
|
25
|
+
except KeyError:
|
|
26
|
+
self._queues[group_id] = asyncio.Queue()
|
|
27
|
+
return self._queues[group_id]
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
from typing import List, Set, Tuple
|
|
4
4
|
|
|
5
|
+
from opentrons_shared_data.module.types import ModuleOrientation
|
|
5
6
|
from opentrons_shared_data.deck.types import (
|
|
6
7
|
DeckDefinitionV5,
|
|
7
8
|
CutoutFixture,
|
|
@@ -124,6 +125,11 @@ def get_addressable_area_from_name(
|
|
|
124
125
|
z=addressable_area["boundingBox"]["zDimension"],
|
|
125
126
|
)
|
|
126
127
|
features = addressable_area["features"]
|
|
128
|
+
orientation = (
|
|
129
|
+
addressable_area["orientation"]
|
|
130
|
+
if addressable_area["orientation"]
|
|
131
|
+
else ModuleOrientation.NOT_APPLICABLE
|
|
132
|
+
)
|
|
127
133
|
mating_surface_unit_vector = addressable_area.get("matingSurfaceUnitVector")
|
|
128
134
|
|
|
129
135
|
return AddressableArea(
|
|
@@ -138,6 +144,7 @@ def get_addressable_area_from_name(
|
|
|
138
144
|
"compatibleModuleTypes", []
|
|
139
145
|
),
|
|
140
146
|
features=features,
|
|
147
|
+
orientation=orientation,
|
|
141
148
|
)
|
|
142
149
|
raise AddressableAreaDoesNotExistError(
|
|
143
150
|
f"Could not find addressable area with name {addressable_area_name}"
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
from opentrons_shared_data.labware.labware_definition import (
|
|
4
4
|
LabwareDefinition,
|
|
5
|
+
LabwareDefinition2,
|
|
5
6
|
LabwareRole,
|
|
6
7
|
)
|
|
7
8
|
|
|
@@ -44,15 +45,18 @@ def validate_definition_is_system(definition: LabwareDefinition) -> bool:
|
|
|
44
45
|
return LabwareRole.system in definition.allowedRoles
|
|
45
46
|
|
|
46
47
|
|
|
47
|
-
def
|
|
48
|
-
|
|
48
|
+
def validate_legacy_labware_can_be_stacked(
|
|
49
|
+
child_labware_definition: LabwareDefinition2, parent_labware_load_name: str
|
|
49
50
|
) -> bool:
|
|
50
|
-
"""Validate that the labware
|
|
51
|
+
"""Validate that the parent labware is in the child labware's stackingOffsetWithLabware definition.
|
|
52
|
+
|
|
53
|
+
Schema 3 Labware stacking validation is handled in locating features.
|
|
54
|
+
"""
|
|
51
55
|
return (
|
|
52
|
-
|
|
56
|
+
parent_labware_load_name in child_labware_definition.stackingOffsetWithLabware
|
|
53
57
|
or (
|
|
54
|
-
"default" in
|
|
55
|
-
and
|
|
58
|
+
"default" in child_labware_definition.stackingOffsetWithLabware
|
|
59
|
+
and child_labware_definition.compatibleParentLabware is None
|
|
56
60
|
)
|
|
57
61
|
)
|
|
58
62
|
|
|
@@ -17,13 +17,47 @@ def wells_covered_by_pipette_configuration(
|
|
|
17
17
|
"""Compute the wells covered by a pipette nozzle configuration."""
|
|
18
18
|
if len(labware_wells_by_column) >= 12 and len(labware_wells_by_column[0]) >= 8:
|
|
19
19
|
yield from wells_covered_dense(
|
|
20
|
-
nozzle_map,
|
|
20
|
+
nozzle_map.columns,
|
|
21
|
+
nozzle_map.rows,
|
|
22
|
+
nozzle_map.starting_nozzle,
|
|
21
23
|
target_well,
|
|
22
24
|
labware_wells_by_column,
|
|
23
25
|
)
|
|
24
26
|
elif len(labware_wells_by_column) < 12 and len(labware_wells_by_column[0]) < 8:
|
|
25
27
|
yield from wells_covered_sparse(
|
|
26
|
-
nozzle_map,
|
|
28
|
+
nozzle_map.columns,
|
|
29
|
+
nozzle_map.rows,
|
|
30
|
+
nozzle_map.starting_nozzle,
|
|
31
|
+
target_well,
|
|
32
|
+
labware_wells_by_column,
|
|
33
|
+
)
|
|
34
|
+
else:
|
|
35
|
+
raise InvalidStoredData(
|
|
36
|
+
"Labware of non-SBS and non-reservoir format cannot be handled"
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def wells_covered_by_physical_pipette(
|
|
41
|
+
nozzle_map: NozzleMap,
|
|
42
|
+
target_well: str,
|
|
43
|
+
labware_wells_by_column: list[list[str]],
|
|
44
|
+
) -> Iterator[str]:
|
|
45
|
+
"""Compute the wells covered by a pipette nozzle configuration."""
|
|
46
|
+
if len(labware_wells_by_column) >= 12 and len(labware_wells_by_column[0]) >= 8:
|
|
47
|
+
yield from wells_covered_dense(
|
|
48
|
+
nozzle_map.full_instrument_columns,
|
|
49
|
+
nozzle_map.full_instrument_rows,
|
|
50
|
+
nozzle_map.starting_nozzle,
|
|
51
|
+
target_well,
|
|
52
|
+
labware_wells_by_column,
|
|
53
|
+
)
|
|
54
|
+
elif len(labware_wells_by_column) < 12 and len(labware_wells_by_column[0]) < 8:
|
|
55
|
+
yield from wells_covered_sparse(
|
|
56
|
+
nozzle_map.full_instrument_columns,
|
|
57
|
+
nozzle_map.full_instrument_rows,
|
|
58
|
+
nozzle_map.starting_nozzle,
|
|
59
|
+
target_well,
|
|
60
|
+
labware_wells_by_column,
|
|
27
61
|
)
|
|
28
62
|
else:
|
|
29
63
|
raise InvalidStoredData(
|
|
@@ -42,7 +76,11 @@ def row_col_ordinals_from_column_major_map(
|
|
|
42
76
|
|
|
43
77
|
|
|
44
78
|
def wells_covered_dense( # noqa: C901
|
|
45
|
-
|
|
79
|
+
columns: dict[str, list[str]],
|
|
80
|
+
rows: dict[str, list[str]],
|
|
81
|
+
starting_nozzle: str,
|
|
82
|
+
target_well: str,
|
|
83
|
+
target_wells_by_column: list[list[str]],
|
|
46
84
|
) -> Iterator[str]:
|
|
47
85
|
"""Get the list of wells covered by a nozzle map on an SBS format labware with a specified multiplier of 96 into the number of wells.
|
|
48
86
|
|
|
@@ -66,11 +104,11 @@ def wells_covered_dense( # noqa: C901
|
|
|
66
104
|
"This labware cannot be used with wells_covered_dense() because it is less dense than an SBS 96 standard"
|
|
67
105
|
)
|
|
68
106
|
|
|
69
|
-
for nozzle_column in range(len(
|
|
107
|
+
for nozzle_column in range(len(columns)):
|
|
70
108
|
target_column_offset = nozzle_column * column_downsample
|
|
71
|
-
for nozzle_row in range(len(
|
|
109
|
+
for nozzle_row in range(len(rows)):
|
|
72
110
|
target_row_offset = nozzle_row * row_downsample
|
|
73
|
-
if
|
|
111
|
+
if starting_nozzle == "A1":
|
|
74
112
|
if (
|
|
75
113
|
target_column_index + target_column_offset
|
|
76
114
|
< len(target_wells_by_column)
|
|
@@ -81,7 +119,7 @@ def wells_covered_dense( # noqa: C901
|
|
|
81
119
|
yield target_wells_by_column[
|
|
82
120
|
target_column_index + target_column_offset
|
|
83
121
|
][target_row_index + target_row_offset]
|
|
84
|
-
elif
|
|
122
|
+
elif starting_nozzle == "A12":
|
|
85
123
|
if (target_column_index - target_column_offset >= 0) and (
|
|
86
124
|
target_row_index + target_row_offset
|
|
87
125
|
< len(target_wells_by_column[target_column_index])
|
|
@@ -89,7 +127,7 @@ def wells_covered_dense( # noqa: C901
|
|
|
89
127
|
yield target_wells_by_column[
|
|
90
128
|
target_column_index - target_column_offset
|
|
91
129
|
][target_row_index + target_row_offset]
|
|
92
|
-
elif
|
|
130
|
+
elif starting_nozzle == "H1":
|
|
93
131
|
if (
|
|
94
132
|
target_column_index + target_column_offset
|
|
95
133
|
< len(target_wells_by_column)
|
|
@@ -97,7 +135,7 @@ def wells_covered_dense( # noqa: C901
|
|
|
97
135
|
yield target_wells_by_column[
|
|
98
136
|
target_column_index + target_column_offset
|
|
99
137
|
][target_row_index - target_row_offset]
|
|
100
|
-
elif
|
|
138
|
+
elif starting_nozzle == "H12":
|
|
101
139
|
if (target_column_index - target_column_offset >= 0) and (
|
|
102
140
|
target_row_index - target_row_offset >= 0
|
|
103
141
|
):
|
|
@@ -106,12 +144,16 @@ def wells_covered_dense( # noqa: C901
|
|
|
106
144
|
][target_row_index - target_row_offset]
|
|
107
145
|
else:
|
|
108
146
|
raise InvalidProtocolData(
|
|
109
|
-
f"A pipette nozzle configuration may not having a starting nozzle of {
|
|
147
|
+
f"A pipette nozzle configuration may not having a starting nozzle of {starting_nozzle}"
|
|
110
148
|
)
|
|
111
149
|
|
|
112
150
|
|
|
113
151
|
def wells_covered_sparse( # noqa: C901
|
|
114
|
-
|
|
152
|
+
columns: dict[str, list[str]],
|
|
153
|
+
rows: dict[str, list[str]],
|
|
154
|
+
starting_nozzle: str,
|
|
155
|
+
target_well: str,
|
|
156
|
+
target_wells_by_column: list[list[str]],
|
|
115
157
|
) -> Iterator[str]:
|
|
116
158
|
"""Get the list of wells covered by a nozzle map on a column-oriented reservoir.
|
|
117
159
|
|
|
@@ -128,9 +170,9 @@ def wells_covered_sparse( # noqa: C901
|
|
|
128
170
|
raise InvalidStoredData(
|
|
129
171
|
"This labware cannot be used with wells_covered_sparse() because it is more dense than an SBS 96 standard."
|
|
130
172
|
)
|
|
131
|
-
for nozzle_column in range(max(1, len(
|
|
132
|
-
for nozzle_row in range(max(1, len(
|
|
133
|
-
if
|
|
173
|
+
for nozzle_column in range(max(1, len(columns) // column_upsample)):
|
|
174
|
+
for nozzle_row in range(max(1, len(rows) // row_upsample)):
|
|
175
|
+
if starting_nozzle == "A1":
|
|
134
176
|
if (
|
|
135
177
|
target_column_index + nozzle_column < len(target_wells_by_column)
|
|
136
178
|
) and (
|
|
@@ -140,7 +182,7 @@ def wells_covered_sparse( # noqa: C901
|
|
|
140
182
|
yield target_wells_by_column[target_column_index + nozzle_column][
|
|
141
183
|
target_row_index + nozzle_row
|
|
142
184
|
]
|
|
143
|
-
elif
|
|
185
|
+
elif starting_nozzle == "A12":
|
|
144
186
|
if (target_column_index - nozzle_column >= 0) and (
|
|
145
187
|
target_row_index + nozzle_row
|
|
146
188
|
< len(target_wells_by_column[target_column_index])
|
|
@@ -148,7 +190,7 @@ def wells_covered_sparse( # noqa: C901
|
|
|
148
190
|
yield target_wells_by_column[target_column_index - nozzle_column][
|
|
149
191
|
target_row_index + nozzle_row
|
|
150
192
|
]
|
|
151
|
-
elif
|
|
193
|
+
elif starting_nozzle == "H1":
|
|
152
194
|
if (
|
|
153
195
|
target_column_index + nozzle_column
|
|
154
196
|
< len(target_wells_by_column[target_column_index])
|
|
@@ -156,7 +198,7 @@ def wells_covered_sparse( # noqa: C901
|
|
|
156
198
|
yield target_wells_by_column[target_column_index + nozzle_column][
|
|
157
199
|
target_row_index - nozzle_row
|
|
158
200
|
]
|
|
159
|
-
elif
|
|
201
|
+
elif starting_nozzle == "H12":
|
|
160
202
|
if (target_column_index - nozzle_column >= 0) and (
|
|
161
203
|
target_row_index - nozzle_row >= 0
|
|
162
204
|
):
|
|
@@ -165,7 +207,7 @@ def wells_covered_sparse( # noqa: C901
|
|
|
165
207
|
]
|
|
166
208
|
else:
|
|
167
209
|
raise InvalidProtocolData(
|
|
168
|
-
f"A pipette nozzle configuration may not having a starting nozzle of {
|
|
210
|
+
f"A pipette nozzle configuration may not having a starting nozzle of {starting_nozzle}"
|
|
169
211
|
)
|
|
170
212
|
|
|
171
213
|
|
|
@@ -5,6 +5,7 @@ from functools import cached_property
|
|
|
5
5
|
from typing import Dict, List, Optional, Set
|
|
6
6
|
|
|
7
7
|
from opentrons_shared_data.robot.types import RobotType, RobotDefinition
|
|
8
|
+
from opentrons_shared_data.module.types import ModuleOrientation
|
|
8
9
|
from opentrons_shared_data.deck.types import (
|
|
9
10
|
DeckDefinitionV5,
|
|
10
11
|
SlotDefV3,
|
|
@@ -614,6 +615,7 @@ class AddressableAreaView:
|
|
|
614
615
|
"displayName": addressable_area.display_name,
|
|
615
616
|
"compatibleModuleTypes": addressable_area.compatible_module_types,
|
|
616
617
|
"features": addressable_area.features,
|
|
618
|
+
"orientation": ModuleOrientation.NOT_APPLICABLE,
|
|
617
619
|
}
|
|
618
620
|
|
|
619
621
|
def get_deck_slot_definitions(self) -> Dict[str, SlotDefV3]:
|
|
@@ -238,7 +238,7 @@ class CommandState:
|
|
|
238
238
|
has_entered_error_recovery: bool
|
|
239
239
|
"""Whether the run has entered error recovery."""
|
|
240
240
|
|
|
241
|
-
|
|
241
|
+
stopped_by_async_error: bool
|
|
242
242
|
"""If this is set to True, the engine was stopped by an estop event."""
|
|
243
243
|
|
|
244
244
|
error_recovery_policy: ErrorRecoveryPolicy
|
|
@@ -272,7 +272,7 @@ class CommandStore(HasState[CommandState], HandlesActions):
|
|
|
272
272
|
run_completed_at=None,
|
|
273
273
|
run_started_at=None,
|
|
274
274
|
latest_protocol_command_hash=None,
|
|
275
|
-
|
|
275
|
+
stopped_by_async_error=False,
|
|
276
276
|
error_recovery_policy=error_recovery_policy,
|
|
277
277
|
has_entered_error_recovery=False,
|
|
278
278
|
)
|
|
@@ -472,8 +472,8 @@ class CommandStore(HasState[CommandState], HandlesActions):
|
|
|
472
472
|
self._state.recovery_target = None
|
|
473
473
|
self._state.queue_status = QueueStatus.PAUSED
|
|
474
474
|
|
|
475
|
-
if action.
|
|
476
|
-
self._state.
|
|
475
|
+
if action.from_asynchronous_error:
|
|
476
|
+
self._state.stopped_by_async_error = True
|
|
477
477
|
self._state.run_result = RunResult.FAILED
|
|
478
478
|
else:
|
|
479
479
|
self._state.run_result = RunResult.STOPPED
|
|
@@ -501,7 +501,7 @@ class CommandStore(HasState[CommandState], HandlesActions):
|
|
|
501
501
|
else:
|
|
502
502
|
# HACK(sf): There needs to be a better way to set
|
|
503
503
|
# an estop error than this else clause
|
|
504
|
-
if self._state.
|
|
504
|
+
if self._state.stopped_by_async_error and action.error_details:
|
|
505
505
|
self._state.run_error = self._map_run_exception_to_error_occurrence(
|
|
506
506
|
action.error_details.error_id,
|
|
507
507
|
action.error_details.created_at,
|
|
@@ -952,9 +952,9 @@ class CommandView:
|
|
|
952
952
|
"""Get whether an engine stop has completed."""
|
|
953
953
|
return self._state.run_completed_at is not None
|
|
954
954
|
|
|
955
|
-
def
|
|
955
|
+
def get_is_stopped_by_async_error(self) -> bool:
|
|
956
956
|
"""Return whether the engine was stopped specifically by an E-stop."""
|
|
957
|
-
return self._state.
|
|
957
|
+
return self._state.stopped_by_async_error
|
|
958
958
|
|
|
959
959
|
def has_been_played(self) -> bool:
|
|
960
960
|
"""Get whether engine has started."""
|