opentrons 8.3.0a0__py2.py3-none-any.whl → 8.3.0a2__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.
- opentrons/calibration_storage/deck_configuration.py +3 -3
- opentrons/calibration_storage/file_operators.py +3 -3
- opentrons/calibration_storage/helpers.py +3 -1
- opentrons/calibration_storage/ot2/models/v1.py +16 -29
- opentrons/calibration_storage/ot2/tip_length.py +7 -4
- opentrons/calibration_storage/ot3/models/v1.py +14 -23
- opentrons/cli/analyze.py +18 -6
- opentrons/drivers/asyncio/communication/__init__.py +2 -0
- opentrons/drivers/asyncio/communication/errors.py +16 -3
- opentrons/drivers/asyncio/communication/serial_connection.py +24 -9
- opentrons/drivers/command_builder.py +2 -2
- opentrons/drivers/flex_stacker/__init__.py +9 -0
- opentrons/drivers/flex_stacker/abstract.py +89 -0
- opentrons/drivers/flex_stacker/driver.py +260 -0
- opentrons/drivers/flex_stacker/simulator.py +109 -0
- opentrons/drivers/flex_stacker/types.py +138 -0
- opentrons/drivers/heater_shaker/driver.py +18 -3
- opentrons/drivers/temp_deck/driver.py +13 -3
- opentrons/drivers/thermocycler/driver.py +17 -3
- opentrons/execute.py +3 -1
- opentrons/hardware_control/__init__.py +1 -2
- opentrons/hardware_control/api.py +28 -20
- opentrons/hardware_control/backends/flex_protocol.py +4 -6
- opentrons/hardware_control/backends/ot3controller.py +177 -59
- opentrons/hardware_control/backends/ot3simulator.py +10 -8
- opentrons/hardware_control/backends/ot3utils.py +3 -13
- opentrons/hardware_control/dev_types.py +2 -0
- opentrons/hardware_control/emulation/heater_shaker.py +4 -0
- opentrons/hardware_control/emulation/module_server/client.py +1 -1
- opentrons/hardware_control/emulation/module_server/server.py +5 -3
- opentrons/hardware_control/emulation/settings.py +3 -4
- opentrons/hardware_control/instruments/ot2/instrument_calibration.py +2 -1
- opentrons/hardware_control/instruments/ot2/pipette.py +9 -21
- opentrons/hardware_control/instruments/ot2/pipette_handler.py +8 -1
- opentrons/hardware_control/instruments/ot3/gripper.py +2 -2
- opentrons/hardware_control/instruments/ot3/pipette.py +13 -22
- opentrons/hardware_control/instruments/ot3/pipette_handler.py +10 -1
- opentrons/hardware_control/modules/mod_abc.py +2 -2
- opentrons/hardware_control/motion_utilities.py +68 -0
- opentrons/hardware_control/nozzle_manager.py +39 -41
- opentrons/hardware_control/ot3_calibration.py +1 -1
- opentrons/hardware_control/ot3api.py +34 -22
- opentrons/hardware_control/protocols/gripper_controller.py +3 -0
- opentrons/hardware_control/protocols/hardware_manager.py +5 -1
- opentrons/hardware_control/protocols/liquid_handler.py +18 -0
- opentrons/hardware_control/protocols/motion_controller.py +6 -0
- opentrons/hardware_control/robot_calibration.py +1 -1
- opentrons/hardware_control/types.py +61 -0
- opentrons/protocol_api/__init__.py +20 -1
- opentrons/protocol_api/_liquid.py +24 -49
- opentrons/protocol_api/_liquid_properties.py +754 -0
- opentrons/protocol_api/_types.py +24 -0
- opentrons/protocol_api/core/common.py +2 -0
- opentrons/protocol_api/core/engine/instrument.py +67 -10
- opentrons/protocol_api/core/engine/labware.py +29 -7
- opentrons/protocol_api/core/engine/protocol.py +130 -5
- opentrons/protocol_api/core/engine/robot.py +139 -0
- opentrons/protocol_api/core/engine/well.py +4 -1
- opentrons/protocol_api/core/instrument.py +42 -4
- opentrons/protocol_api/core/labware.py +13 -4
- opentrons/protocol_api/core/legacy/legacy_instrument_core.py +34 -3
- opentrons/protocol_api/core/legacy/legacy_labware_core.py +13 -4
- opentrons/protocol_api/core/legacy/legacy_protocol_core.py +32 -1
- opentrons/protocol_api/core/legacy/legacy_robot_core.py +0 -0
- opentrons/protocol_api/core/legacy_simulator/legacy_instrument_core.py +34 -3
- opentrons/protocol_api/core/protocol.py +34 -1
- opentrons/protocol_api/core/robot.py +51 -0
- opentrons/protocol_api/instrument_context.py +145 -43
- opentrons/protocol_api/labware.py +231 -7
- opentrons/protocol_api/module_contexts.py +21 -17
- opentrons/protocol_api/protocol_context.py +125 -4
- opentrons/protocol_api/robot_context.py +204 -32
- opentrons/protocol_api/validation.py +261 -3
- opentrons/protocol_engine/__init__.py +4 -0
- opentrons/protocol_engine/actions/actions.py +2 -3
- opentrons/protocol_engine/clients/sync_client.py +18 -0
- opentrons/protocol_engine/commands/__init__.py +81 -0
- opentrons/protocol_engine/commands/absorbance_reader/close_lid.py +0 -2
- opentrons/protocol_engine/commands/absorbance_reader/initialize.py +19 -5
- opentrons/protocol_engine/commands/absorbance_reader/open_lid.py +0 -1
- opentrons/protocol_engine/commands/absorbance_reader/read.py +32 -9
- opentrons/protocol_engine/commands/air_gap_in_place.py +160 -0
- opentrons/protocol_engine/commands/aspirate.py +103 -53
- opentrons/protocol_engine/commands/aspirate_in_place.py +55 -51
- opentrons/protocol_engine/commands/blow_out.py +44 -39
- opentrons/protocol_engine/commands/blow_out_in_place.py +21 -32
- opentrons/protocol_engine/commands/calibration/calibrate_gripper.py +13 -6
- opentrons/protocol_engine/commands/calibration/calibrate_module.py +1 -1
- opentrons/protocol_engine/commands/calibration/calibrate_pipette.py +3 -3
- opentrons/protocol_engine/commands/calibration/move_to_maintenance_position.py +1 -1
- opentrons/protocol_engine/commands/command.py +73 -66
- opentrons/protocol_engine/commands/command_unions.py +101 -1
- opentrons/protocol_engine/commands/comment.py +1 -1
- opentrons/protocol_engine/commands/configure_for_volume.py +10 -3
- opentrons/protocol_engine/commands/configure_nozzle_layout.py +6 -4
- opentrons/protocol_engine/commands/custom.py +6 -12
- opentrons/protocol_engine/commands/dispense.py +82 -48
- opentrons/protocol_engine/commands/dispense_in_place.py +71 -51
- opentrons/protocol_engine/commands/drop_tip.py +52 -31
- opentrons/protocol_engine/commands/drop_tip_in_place.py +13 -3
- opentrons/protocol_engine/commands/generate_command_schema.py +4 -11
- opentrons/protocol_engine/commands/get_next_tip.py +134 -0
- opentrons/protocol_engine/commands/get_tip_presence.py +1 -1
- opentrons/protocol_engine/commands/heater_shaker/close_labware_latch.py +1 -1
- opentrons/protocol_engine/commands/heater_shaker/deactivate_heater.py +1 -1
- opentrons/protocol_engine/commands/heater_shaker/deactivate_shaker.py +1 -1
- opentrons/protocol_engine/commands/heater_shaker/open_labware_latch.py +1 -1
- opentrons/protocol_engine/commands/heater_shaker/set_and_wait_for_shake_speed.py +1 -1
- opentrons/protocol_engine/commands/heater_shaker/set_target_temperature.py +1 -1
- opentrons/protocol_engine/commands/heater_shaker/wait_for_temperature.py +10 -4
- opentrons/protocol_engine/commands/home.py +13 -4
- opentrons/protocol_engine/commands/liquid_probe.py +60 -25
- opentrons/protocol_engine/commands/load_labware.py +29 -7
- opentrons/protocol_engine/commands/load_lid.py +146 -0
- opentrons/protocol_engine/commands/load_lid_stack.py +189 -0
- opentrons/protocol_engine/commands/load_liquid.py +12 -4
- opentrons/protocol_engine/commands/load_liquid_class.py +144 -0
- opentrons/protocol_engine/commands/load_module.py +31 -10
- opentrons/protocol_engine/commands/load_pipette.py +19 -8
- opentrons/protocol_engine/commands/magnetic_module/disengage.py +1 -1
- opentrons/protocol_engine/commands/magnetic_module/engage.py +1 -1
- opentrons/protocol_engine/commands/move_labware.py +19 -6
- opentrons/protocol_engine/commands/move_relative.py +35 -25
- opentrons/protocol_engine/commands/move_to_addressable_area.py +40 -27
- opentrons/protocol_engine/commands/move_to_addressable_area_for_drop_tip.py +53 -32
- opentrons/protocol_engine/commands/move_to_coordinates.py +36 -22
- opentrons/protocol_engine/commands/move_to_well.py +40 -24
- opentrons/protocol_engine/commands/movement_common.py +338 -0
- opentrons/protocol_engine/commands/pick_up_tip.py +49 -27
- opentrons/protocol_engine/commands/pipetting_common.py +169 -87
- opentrons/protocol_engine/commands/prepare_to_aspirate.py +24 -33
- opentrons/protocol_engine/commands/reload_labware.py +1 -1
- opentrons/protocol_engine/commands/retract_axis.py +1 -1
- opentrons/protocol_engine/commands/robot/__init__.py +69 -0
- opentrons/protocol_engine/commands/robot/close_gripper_jaw.py +86 -0
- opentrons/protocol_engine/commands/robot/common.py +18 -0
- opentrons/protocol_engine/commands/robot/move_axes_relative.py +101 -0
- opentrons/protocol_engine/commands/robot/move_axes_to.py +100 -0
- opentrons/protocol_engine/commands/robot/move_to.py +94 -0
- opentrons/protocol_engine/commands/robot/open_gripper_jaw.py +77 -0
- opentrons/protocol_engine/commands/save_position.py +14 -5
- opentrons/protocol_engine/commands/set_rail_lights.py +1 -1
- opentrons/protocol_engine/commands/set_status_bar.py +1 -1
- opentrons/protocol_engine/commands/temperature_module/deactivate.py +1 -1
- opentrons/protocol_engine/commands/temperature_module/set_target_temperature.py +1 -1
- opentrons/protocol_engine/commands/temperature_module/wait_for_temperature.py +10 -4
- opentrons/protocol_engine/commands/thermocycler/close_lid.py +1 -1
- opentrons/protocol_engine/commands/thermocycler/deactivate_block.py +1 -1
- opentrons/protocol_engine/commands/thermocycler/deactivate_lid.py +1 -1
- opentrons/protocol_engine/commands/thermocycler/open_lid.py +1 -1
- opentrons/protocol_engine/commands/thermocycler/run_extended_profile.py +8 -2
- opentrons/protocol_engine/commands/thermocycler/run_profile.py +9 -3
- opentrons/protocol_engine/commands/thermocycler/set_target_block_temperature.py +11 -4
- opentrons/protocol_engine/commands/thermocycler/set_target_lid_temperature.py +1 -1
- opentrons/protocol_engine/commands/thermocycler/wait_for_block_temperature.py +1 -1
- opentrons/protocol_engine/commands/thermocycler/wait_for_lid_temperature.py +1 -1
- opentrons/protocol_engine/commands/touch_tip.py +65 -16
- opentrons/protocol_engine/commands/unsafe/unsafe_blow_out_in_place.py +4 -1
- opentrons/protocol_engine/commands/unsafe/unsafe_drop_tip_in_place.py +12 -3
- opentrons/protocol_engine/commands/unsafe/unsafe_engage_axes.py +1 -4
- opentrons/protocol_engine/commands/unsafe/update_position_estimators.py +1 -4
- opentrons/protocol_engine/commands/verify_tip_presence.py +11 -4
- opentrons/protocol_engine/commands/wait_for_duration.py +10 -3
- opentrons/protocol_engine/commands/wait_for_resume.py +10 -3
- opentrons/protocol_engine/errors/__init__.py +8 -0
- opentrons/protocol_engine/errors/error_occurrence.py +19 -20
- opentrons/protocol_engine/errors/exceptions.py +50 -0
- opentrons/protocol_engine/execution/command_executor.py +1 -1
- opentrons/protocol_engine/execution/equipment.py +73 -5
- opentrons/protocol_engine/execution/gantry_mover.py +364 -8
- opentrons/protocol_engine/execution/movement.py +27 -0
- opentrons/protocol_engine/execution/pipetting.py +5 -1
- opentrons/protocol_engine/execution/tip_handler.py +4 -6
- opentrons/protocol_engine/notes/notes.py +1 -1
- opentrons/protocol_engine/protocol_engine.py +7 -6
- opentrons/protocol_engine/resources/labware_data_provider.py +1 -1
- opentrons/protocol_engine/resources/labware_validation.py +5 -0
- opentrons/protocol_engine/resources/module_data_provider.py +1 -1
- opentrons/protocol_engine/resources/pipette_data_provider.py +12 -0
- opentrons/protocol_engine/slot_standardization.py +9 -9
- opentrons/protocol_engine/state/_move_types.py +9 -5
- opentrons/protocol_engine/state/_well_math.py +193 -0
- opentrons/protocol_engine/state/addressable_areas.py +25 -61
- opentrons/protocol_engine/state/command_history.py +12 -0
- opentrons/protocol_engine/state/commands.py +17 -13
- opentrons/protocol_engine/state/files.py +10 -12
- opentrons/protocol_engine/state/fluid_stack.py +138 -0
- opentrons/protocol_engine/state/frustum_helpers.py +57 -32
- opentrons/protocol_engine/state/geometry.py +47 -1
- opentrons/protocol_engine/state/labware.py +79 -25
- opentrons/protocol_engine/state/liquid_classes.py +82 -0
- opentrons/protocol_engine/state/liquids.py +16 -4
- opentrons/protocol_engine/state/modules.py +52 -70
- opentrons/protocol_engine/state/motion.py +6 -1
- opentrons/protocol_engine/state/pipettes.py +135 -58
- opentrons/protocol_engine/state/state.py +21 -2
- opentrons/protocol_engine/state/state_summary.py +4 -2
- opentrons/protocol_engine/state/tips.py +11 -44
- opentrons/protocol_engine/state/update_types.py +343 -48
- opentrons/protocol_engine/state/wells.py +19 -11
- opentrons/protocol_engine/types.py +176 -28
- opentrons/protocol_reader/extract_labware_definitions.py +5 -2
- opentrons/protocol_reader/file_format_validator.py +5 -5
- opentrons/protocol_runner/json_file_reader.py +9 -3
- opentrons/protocol_runner/json_translator.py +51 -25
- opentrons/protocol_runner/legacy_command_mapper.py +66 -64
- opentrons/protocol_runner/protocol_runner.py +35 -4
- opentrons/protocol_runner/python_protocol_wrappers.py +1 -1
- opentrons/protocol_runner/run_orchestrator.py +13 -3
- opentrons/protocols/advanced_control/common.py +38 -0
- opentrons/protocols/advanced_control/mix.py +1 -1
- opentrons/protocols/advanced_control/transfers/__init__.py +0 -0
- opentrons/protocols/advanced_control/transfers/common.py +56 -0
- opentrons/protocols/advanced_control/{transfers.py → transfers/transfer.py} +10 -85
- opentrons/protocols/api_support/definitions.py +1 -1
- opentrons/protocols/api_support/instrument.py +1 -1
- opentrons/protocols/api_support/util.py +10 -0
- opentrons/protocols/labware.py +70 -8
- opentrons/protocols/models/json_protocol.py +5 -9
- opentrons/simulate.py +3 -1
- opentrons/types.py +162 -2
- opentrons/util/entrypoint_util.py +2 -5
- opentrons/util/logging_config.py +1 -1
- {opentrons-8.3.0a0.dist-info → opentrons-8.3.0a2.dist-info}/METADATA +16 -15
- {opentrons-8.3.0a0.dist-info → opentrons-8.3.0a2.dist-info}/RECORD +229 -202
- {opentrons-8.3.0a0.dist-info → opentrons-8.3.0a2.dist-info}/WHEEL +1 -1
- {opentrons-8.3.0a0.dist-info → opentrons-8.3.0a2.dist-info}/LICENSE +0 -0
- {opentrons-8.3.0a0.dist-info → opentrons-8.3.0a2.dist-info}/entry_points.txt +0 -0
- {opentrons-8.3.0a0.dist-info → opentrons-8.3.0a2.dist-info}/top_level.txt +0 -0
|
@@ -1,29 +1,50 @@
|
|
|
1
1
|
"""Touch tip command request, result, and implementation models."""
|
|
2
|
+
|
|
2
3
|
from __future__ import annotations
|
|
3
|
-
from
|
|
4
|
-
|
|
4
|
+
from typing import TYPE_CHECKING, Optional, Type, Any
|
|
5
|
+
|
|
5
6
|
from typing_extensions import Literal
|
|
7
|
+
from pydantic import Field
|
|
8
|
+
from pydantic.json_schema import SkipJsonSchema
|
|
6
9
|
|
|
7
|
-
from opentrons.
|
|
10
|
+
from opentrons.types import Point
|
|
8
11
|
|
|
9
|
-
from ..errors import
|
|
12
|
+
from ..errors import (
|
|
13
|
+
TouchTipDisabledError,
|
|
14
|
+
TouchTipIncompatibleArgumentsError,
|
|
15
|
+
LabwareIsTipRackError,
|
|
16
|
+
)
|
|
10
17
|
from ..types import DeckPoint
|
|
11
|
-
from .command import
|
|
12
|
-
|
|
18
|
+
from .command import (
|
|
19
|
+
AbstractCommandImpl,
|
|
20
|
+
BaseCommand,
|
|
21
|
+
BaseCommandCreate,
|
|
22
|
+
SuccessData,
|
|
23
|
+
DefinedErrorData,
|
|
24
|
+
)
|
|
13
25
|
from .pipetting_common import (
|
|
14
26
|
PipetteIdMixin,
|
|
27
|
+
)
|
|
28
|
+
from .movement_common import (
|
|
15
29
|
WellLocationMixin,
|
|
16
30
|
DestinationPositionResult,
|
|
31
|
+
StallOrCollisionError,
|
|
32
|
+
move_to_well,
|
|
17
33
|
)
|
|
18
34
|
|
|
19
35
|
if TYPE_CHECKING:
|
|
20
36
|
from ..execution import MovementHandler, GantryMover
|
|
21
37
|
from ..state.state import StateView
|
|
38
|
+
from ..resources.model_utils import ModelUtils
|
|
22
39
|
|
|
23
40
|
|
|
24
41
|
TouchTipCommandType = Literal["touchTip"]
|
|
25
42
|
|
|
26
43
|
|
|
44
|
+
def _remove_default(s: dict[str, Any]) -> None:
|
|
45
|
+
s.pop("default", None)
|
|
46
|
+
|
|
47
|
+
|
|
27
48
|
class TouchTipParams(PipetteIdMixin, WellLocationMixin):
|
|
28
49
|
"""Payload needed to touch a pipette tip the sides of a specific well."""
|
|
29
50
|
|
|
@@ -34,12 +55,20 @@ class TouchTipParams(PipetteIdMixin, WellLocationMixin):
|
|
|
34
55
|
),
|
|
35
56
|
)
|
|
36
57
|
|
|
37
|
-
|
|
58
|
+
mmFromEdge: float | SkipJsonSchema[None] = Field(
|
|
59
|
+
None,
|
|
60
|
+
description="Offset away from the the well edge, in millimeters."
|
|
61
|
+
"Incompatible when a radius is included as a non 1.0 value.",
|
|
62
|
+
json_schema_extra=_remove_default,
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
speed: float | SkipJsonSchema[None] = Field(
|
|
38
66
|
None,
|
|
39
67
|
description=(
|
|
40
68
|
"Override the travel speed in mm/s."
|
|
41
69
|
" This controls the straight linear speed of motion."
|
|
42
70
|
),
|
|
71
|
+
json_schema_extra=_remove_default,
|
|
43
72
|
)
|
|
44
73
|
|
|
45
74
|
|
|
@@ -50,7 +79,10 @@ class TouchTipResult(DestinationPositionResult):
|
|
|
50
79
|
|
|
51
80
|
|
|
52
81
|
class TouchTipImplementation(
|
|
53
|
-
AbstractCommandImpl[
|
|
82
|
+
AbstractCommandImpl[
|
|
83
|
+
TouchTipParams,
|
|
84
|
+
SuccessData[TouchTipResult] | DefinedErrorData[StallOrCollisionError],
|
|
85
|
+
]
|
|
54
86
|
):
|
|
55
87
|
"""Touch tip command implementation."""
|
|
56
88
|
|
|
@@ -59,19 +91,26 @@ class TouchTipImplementation(
|
|
|
59
91
|
state_view: StateView,
|
|
60
92
|
movement: MovementHandler,
|
|
61
93
|
gantry_mover: GantryMover,
|
|
94
|
+
model_utils: ModelUtils,
|
|
62
95
|
**kwargs: object,
|
|
63
96
|
) -> None:
|
|
64
97
|
self._state_view = state_view
|
|
65
98
|
self._movement = movement
|
|
66
99
|
self._gantry_mover = gantry_mover
|
|
100
|
+
self._model_utils = model_utils
|
|
67
101
|
|
|
68
|
-
async def execute(
|
|
102
|
+
async def execute(
|
|
103
|
+
self, params: TouchTipParams
|
|
104
|
+
) -> SuccessData[TouchTipResult] | DefinedErrorData[StallOrCollisionError]:
|
|
69
105
|
"""Touch tip to sides of a well using the requested pipette."""
|
|
70
106
|
pipette_id = params.pipetteId
|
|
71
107
|
labware_id = params.labwareId
|
|
72
108
|
well_name = params.wellName
|
|
73
109
|
|
|
74
|
-
|
|
110
|
+
if params.radius != 1.0 and params.mmFromEdge is not None:
|
|
111
|
+
raise TouchTipIncompatibleArgumentsError(
|
|
112
|
+
"Cannot use mmFromEdge with a radius that is not 1.0"
|
|
113
|
+
)
|
|
75
114
|
|
|
76
115
|
if self._state_view.labware.get_has_quirk(labware_id, "touchTipDisabled"):
|
|
77
116
|
raise TouchTipDisabledError(
|
|
@@ -81,23 +120,33 @@ class TouchTipImplementation(
|
|
|
81
120
|
if self._state_view.labware.is_tiprack(labware_id):
|
|
82
121
|
raise LabwareIsTipRackError("Cannot touch tip on tip rack")
|
|
83
122
|
|
|
84
|
-
|
|
123
|
+
center_result = await move_to_well(
|
|
124
|
+
movement=self._movement,
|
|
125
|
+
model_utils=self._model_utils,
|
|
85
126
|
pipette_id=pipette_id,
|
|
86
127
|
labware_id=labware_id,
|
|
87
128
|
well_name=well_name,
|
|
88
129
|
well_location=params.wellLocation,
|
|
89
130
|
)
|
|
131
|
+
if isinstance(center_result, DefinedErrorData):
|
|
132
|
+
return center_result
|
|
90
133
|
|
|
91
134
|
touch_speed = self._state_view.pipettes.get_movement_speed(
|
|
92
135
|
pipette_id, params.speed
|
|
93
136
|
)
|
|
94
137
|
|
|
138
|
+
mm_from_edge = params.mmFromEdge if params.mmFromEdge is not None else 0
|
|
95
139
|
touch_waypoints = self._state_view.motion.get_touch_tip_waypoints(
|
|
96
140
|
pipette_id=pipette_id,
|
|
97
141
|
labware_id=labware_id,
|
|
98
142
|
well_name=well_name,
|
|
99
143
|
radius=params.radius,
|
|
100
|
-
|
|
144
|
+
mm_from_edge=mm_from_edge,
|
|
145
|
+
center_point=Point(
|
|
146
|
+
center_result.public.position.x,
|
|
147
|
+
center_result.public.position.y,
|
|
148
|
+
center_result.public.position.z,
|
|
149
|
+
),
|
|
101
150
|
)
|
|
102
151
|
|
|
103
152
|
final_point = await self._gantry_mover.move_to(
|
|
@@ -105,10 +154,10 @@ class TouchTipImplementation(
|
|
|
105
154
|
waypoints=touch_waypoints,
|
|
106
155
|
speed=touch_speed,
|
|
107
156
|
)
|
|
108
|
-
final_deck_point = DeckPoint.
|
|
157
|
+
final_deck_point = DeckPoint.model_construct(
|
|
109
158
|
x=final_point.x, y=final_point.y, z=final_point.z
|
|
110
159
|
)
|
|
111
|
-
state_update.set_pipette_location(
|
|
160
|
+
state_update = center_result.state_update.set_pipette_location(
|
|
112
161
|
pipette_id=pipette_id,
|
|
113
162
|
new_labware_id=labware_id,
|
|
114
163
|
new_well_name=well_name,
|
|
@@ -121,12 +170,12 @@ class TouchTipImplementation(
|
|
|
121
170
|
)
|
|
122
171
|
|
|
123
172
|
|
|
124
|
-
class TouchTip(BaseCommand[TouchTipParams, TouchTipResult,
|
|
173
|
+
class TouchTip(BaseCommand[TouchTipParams, TouchTipResult, StallOrCollisionError]):
|
|
125
174
|
"""Touch up tip command model."""
|
|
126
175
|
|
|
127
176
|
commandType: TouchTipCommandType = "touchTip"
|
|
128
177
|
params: TouchTipParams
|
|
129
|
-
result: Optional[TouchTipResult]
|
|
178
|
+
result: Optional[TouchTipResult] = None
|
|
130
179
|
|
|
131
180
|
_ImplementationCls: Type[TouchTipImplementation] = TouchTipImplementation
|
|
132
181
|
|
|
@@ -10,6 +10,7 @@ from ..command import AbstractCommandImpl, BaseCommand, BaseCommandCreate, Succe
|
|
|
10
10
|
from ..pipetting_common import PipetteIdMixin, FlowRateMixin
|
|
11
11
|
from ...resources import ensure_ot3_hardware
|
|
12
12
|
from ...errors.error_occurrence import ErrorOccurrence
|
|
13
|
+
from ...state import update_types
|
|
13
14
|
|
|
14
15
|
from opentrons.hardware_control import HardwareControlAPI
|
|
15
16
|
from opentrons.hardware_control.types import Axis
|
|
@@ -66,9 +67,11 @@ class UnsafeBlowOutInPlaceImplementation(
|
|
|
66
67
|
await self._pipetting.blow_out_in_place(
|
|
67
68
|
pipette_id=params.pipetteId, flow_rate=params.flowRate
|
|
68
69
|
)
|
|
70
|
+
state_update = update_types.StateUpdate()
|
|
71
|
+
state_update.set_fluid_empty(pipette_id=params.pipetteId)
|
|
69
72
|
|
|
70
73
|
return SuccessData(
|
|
71
|
-
public=UnsafeBlowOutInPlaceResult(),
|
|
74
|
+
public=UnsafeBlowOutInPlaceResult(), state_update=state_update
|
|
72
75
|
)
|
|
73
76
|
|
|
74
77
|
|
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
"""Command models to drop tip in place while plunger positions are unknown."""
|
|
2
|
+
|
|
2
3
|
from __future__ import annotations
|
|
3
|
-
from
|
|
4
|
+
from typing import TYPE_CHECKING, Optional, Type, Any
|
|
5
|
+
|
|
4
6
|
from pydantic import Field, BaseModel
|
|
5
|
-
from
|
|
7
|
+
from pydantic.json_schema import SkipJsonSchema
|
|
6
8
|
from typing_extensions import Literal
|
|
7
9
|
|
|
8
10
|
from opentrons.hardware_control import HardwareControlAPI
|
|
9
11
|
from opentrons.hardware_control.types import Axis
|
|
10
12
|
|
|
13
|
+
from opentrons.protocol_engine.state.update_types import StateUpdate
|
|
11
14
|
from ..pipetting_common import PipetteIdMixin
|
|
12
15
|
from ..command import AbstractCommandImpl, BaseCommand, BaseCommandCreate, SuccessData
|
|
13
16
|
from ...errors.error_occurrence import ErrorOccurrence
|
|
@@ -21,16 +24,21 @@ if TYPE_CHECKING:
|
|
|
21
24
|
UnsafeDropTipInPlaceCommandType = Literal["unsafe/dropTipInPlace"]
|
|
22
25
|
|
|
23
26
|
|
|
27
|
+
def _remove_default(s: dict[str, Any]) -> None:
|
|
28
|
+
s.pop("default", None)
|
|
29
|
+
|
|
30
|
+
|
|
24
31
|
class UnsafeDropTipInPlaceParams(PipetteIdMixin):
|
|
25
32
|
"""Payload required to drop a tip in place even if the plunger position is not known."""
|
|
26
33
|
|
|
27
|
-
homeAfter:
|
|
34
|
+
homeAfter: bool | SkipJsonSchema[None] = Field(
|
|
28
35
|
None,
|
|
29
36
|
description=(
|
|
30
37
|
"Whether to home this pipette's plunger after dropping the tip."
|
|
31
38
|
" You should normally leave this unspecified to let the robot choose"
|
|
32
39
|
" a safe default depending on its hardware."
|
|
33
40
|
),
|
|
41
|
+
json_schema_extra=_remove_default,
|
|
34
42
|
)
|
|
35
43
|
|
|
36
44
|
|
|
@@ -77,6 +85,7 @@ class UnsafeDropTipInPlaceImplementation(
|
|
|
77
85
|
state_update.update_pipette_tip_state(
|
|
78
86
|
pipette_id=params.pipetteId, tip_geometry=None
|
|
79
87
|
)
|
|
88
|
+
state_update.set_fluid_unknown(pipette_id=params.pipetteId)
|
|
80
89
|
|
|
81
90
|
return SuccessData(
|
|
82
91
|
public=UnsafeDropTipInPlaceResult(), state_update=state_update
|
|
@@ -52,10 +52,7 @@ class UnsafeEngageAxesImplementation(
|
|
|
52
52
|
"""Enable exes."""
|
|
53
53
|
ot3_hardware_api = ensure_ot3_hardware(self._hardware_api)
|
|
54
54
|
await ot3_hardware_api.engage_axes(
|
|
55
|
-
|
|
56
|
-
self._gantry_mover.motor_axis_to_hardware_axis(axis)
|
|
57
|
-
for axis in params.axes
|
|
58
|
-
]
|
|
55
|
+
self._gantry_mover.motor_axes_to_present_hardware_axes(params.axes)
|
|
59
56
|
)
|
|
60
57
|
return SuccessData(
|
|
61
58
|
public=UnsafeEngageAxesResult(),
|
|
@@ -58,10 +58,7 @@ class UpdatePositionEstimatorsImplementation(
|
|
|
58
58
|
"""Update axis position estimators from their encoders."""
|
|
59
59
|
ot3_hardware_api = ensure_ot3_hardware(self._hardware_api)
|
|
60
60
|
await ot3_hardware_api.update_axis_position_estimations(
|
|
61
|
-
|
|
62
|
-
self._gantry_mover.motor_axis_to_hardware_axis(axis)
|
|
63
|
-
for axis in params.axes
|
|
64
|
-
]
|
|
61
|
+
self._gantry_mover.motor_axes_to_present_hardware_axes(params.axes)
|
|
65
62
|
)
|
|
66
63
|
return SuccessData(
|
|
67
64
|
public=UpdatePositionEstimatorsResult(),
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
"""Verify tip presence command request, result and implementation models."""
|
|
2
2
|
from __future__ import annotations
|
|
3
|
+
from typing import TYPE_CHECKING, Optional, Type, Any
|
|
3
4
|
|
|
4
5
|
from pydantic import Field, BaseModel
|
|
5
|
-
from
|
|
6
|
+
from pydantic.json_schema import SkipJsonSchema
|
|
6
7
|
from typing_extensions import Literal
|
|
7
8
|
|
|
8
9
|
from .pipetting_common import PipetteIdMixin
|
|
@@ -18,14 +19,20 @@ if TYPE_CHECKING:
|
|
|
18
19
|
VerifyTipPresenceCommandType = Literal["verifyTipPresence"]
|
|
19
20
|
|
|
20
21
|
|
|
22
|
+
def _remove_default(s: dict[str, Any]) -> None:
|
|
23
|
+
s.pop("default", None)
|
|
24
|
+
|
|
25
|
+
|
|
21
26
|
class VerifyTipPresenceParams(PipetteIdMixin):
|
|
22
27
|
"""Payload required for a VerifyTipPresence command."""
|
|
23
28
|
|
|
24
29
|
expectedState: TipPresenceStatus = Field(
|
|
25
30
|
..., description="The expected tip presence status on the pipette."
|
|
26
31
|
)
|
|
27
|
-
followSingularSensor:
|
|
28
|
-
default=None,
|
|
32
|
+
followSingularSensor: InstrumentSensorId | SkipJsonSchema[None] = Field(
|
|
33
|
+
default=None,
|
|
34
|
+
description="The sensor id to follow if the other can be ignored.",
|
|
35
|
+
json_schema_extra=_remove_default,
|
|
29
36
|
)
|
|
30
37
|
|
|
31
38
|
|
|
@@ -77,7 +84,7 @@ class VerifyTipPresence(
|
|
|
77
84
|
|
|
78
85
|
commandType: VerifyTipPresenceCommandType = "verifyTipPresence"
|
|
79
86
|
params: VerifyTipPresenceParams
|
|
80
|
-
result: Optional[VerifyTipPresenceResult]
|
|
87
|
+
result: Optional[VerifyTipPresenceResult] = None
|
|
81
88
|
|
|
82
89
|
_ImplementationCls: Type[
|
|
83
90
|
VerifyTipPresenceImplementation
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
"""Wait for duration command request, result, and implementation models."""
|
|
2
2
|
from __future__ import annotations
|
|
3
|
+
from typing import TYPE_CHECKING, Optional, Type, Any
|
|
4
|
+
|
|
3
5
|
from pydantic import BaseModel, Field
|
|
4
|
-
from
|
|
6
|
+
from pydantic.json_schema import SkipJsonSchema
|
|
5
7
|
from typing_extensions import Literal
|
|
6
8
|
|
|
7
9
|
from .command import AbstractCommandImpl, BaseCommand, BaseCommandCreate, SuccessData
|
|
@@ -14,13 +16,18 @@ if TYPE_CHECKING:
|
|
|
14
16
|
WaitForDurationCommandType = Literal["waitForDuration"]
|
|
15
17
|
|
|
16
18
|
|
|
19
|
+
def _remove_default(s: dict[str, Any]) -> None:
|
|
20
|
+
s.pop("default", None)
|
|
21
|
+
|
|
22
|
+
|
|
17
23
|
class WaitForDurationParams(BaseModel):
|
|
18
24
|
"""Payload required to pause the protocol."""
|
|
19
25
|
|
|
20
26
|
seconds: float = Field(..., description="Duration, in seconds, to wait for.")
|
|
21
|
-
message:
|
|
27
|
+
message: str | SkipJsonSchema[None] = Field(
|
|
22
28
|
None,
|
|
23
29
|
description="A user-facing message associated with the pause",
|
|
30
|
+
json_schema_extra=_remove_default,
|
|
24
31
|
)
|
|
25
32
|
|
|
26
33
|
|
|
@@ -53,7 +60,7 @@ class WaitForDuration(
|
|
|
53
60
|
|
|
54
61
|
commandType: WaitForDurationCommandType = "waitForDuration"
|
|
55
62
|
params: WaitForDurationParams
|
|
56
|
-
result: Optional[WaitForDurationResult]
|
|
63
|
+
result: Optional[WaitForDurationResult] = None
|
|
57
64
|
|
|
58
65
|
_ImplementationCls: Type[
|
|
59
66
|
WaitForDurationImplementation
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
"""Wait for resume command request, result, and implementation models."""
|
|
2
2
|
from __future__ import annotations
|
|
3
|
+
from typing import TYPE_CHECKING, Optional, Type, Any
|
|
4
|
+
|
|
3
5
|
from pydantic import BaseModel, Field
|
|
4
|
-
from
|
|
6
|
+
from pydantic.json_schema import SkipJsonSchema
|
|
5
7
|
from typing_extensions import Literal
|
|
6
8
|
|
|
7
9
|
from .command import AbstractCommandImpl, BaseCommand, BaseCommandCreate, SuccessData
|
|
@@ -16,12 +18,17 @@ if TYPE_CHECKING:
|
|
|
16
18
|
WaitForResumeCommandType = Literal["waitForResume", "pause"]
|
|
17
19
|
|
|
18
20
|
|
|
21
|
+
def _remove_default(s: dict[str, Any]) -> None:
|
|
22
|
+
s.pop("default", None)
|
|
23
|
+
|
|
24
|
+
|
|
19
25
|
class WaitForResumeParams(BaseModel):
|
|
20
26
|
"""Payload required to pause the protocol."""
|
|
21
27
|
|
|
22
|
-
message:
|
|
28
|
+
message: str | SkipJsonSchema[None] = Field(
|
|
23
29
|
None,
|
|
24
30
|
description="A user-facing message associated with the pause",
|
|
31
|
+
json_schema_extra=_remove_default,
|
|
25
32
|
)
|
|
26
33
|
|
|
27
34
|
|
|
@@ -54,7 +61,7 @@ class WaitForResume(
|
|
|
54
61
|
|
|
55
62
|
commandType: WaitForResumeCommandType = "waitForResume"
|
|
56
63
|
params: WaitForResumeParams
|
|
57
|
-
result: Optional[WaitForResumeResult]
|
|
64
|
+
result: Optional[WaitForResumeResult] = None
|
|
58
65
|
|
|
59
66
|
_ImplementationCls: Type[WaitForResumeImplementation] = WaitForResumeImplementation
|
|
60
67
|
|
|
@@ -24,6 +24,7 @@ from .exceptions import (
|
|
|
24
24
|
LabwareIsTipRackError,
|
|
25
25
|
LabwareIsAdapterError,
|
|
26
26
|
TouchTipDisabledError,
|
|
27
|
+
TouchTipIncompatibleArgumentsError,
|
|
27
28
|
WellDoesNotExistError,
|
|
28
29
|
PipetteNotLoadedError,
|
|
29
30
|
ModuleNotLoadedError,
|
|
@@ -78,6 +79,9 @@ from .exceptions import (
|
|
|
78
79
|
OperationLocationNotInWellError,
|
|
79
80
|
InvalidDispenseVolumeError,
|
|
80
81
|
StorageLimitReachedError,
|
|
82
|
+
InvalidLiquidError,
|
|
83
|
+
LiquidClassDoesNotExistError,
|
|
84
|
+
LiquidClassRedefinitionError,
|
|
81
85
|
)
|
|
82
86
|
|
|
83
87
|
from .error_occurrence import ErrorOccurrence, ProtocolCommandFailedError
|
|
@@ -107,6 +111,7 @@ __all__ = [
|
|
|
107
111
|
"LabwareIsTipRackError",
|
|
108
112
|
"LabwareIsAdapterError",
|
|
109
113
|
"TouchTipDisabledError",
|
|
114
|
+
"TouchTipIncompatibleArgumentsError",
|
|
110
115
|
"WellDoesNotExistError",
|
|
111
116
|
"PipetteNotLoadedError",
|
|
112
117
|
"ModuleNotLoadedError",
|
|
@@ -138,6 +143,7 @@ __all__ = [
|
|
|
138
143
|
"InvalidTargetSpeedError",
|
|
139
144
|
"InvalidBlockVolumeError",
|
|
140
145
|
"InvalidHoldTimeError",
|
|
146
|
+
"InvalidLiquidError",
|
|
141
147
|
"InvalidWavelengthError",
|
|
142
148
|
"CannotPerformModuleAction",
|
|
143
149
|
"ResumeFromRecoveryNotAllowedError",
|
|
@@ -164,4 +170,6 @@ __all__ = [
|
|
|
164
170
|
"OperationLocationNotInWellError",
|
|
165
171
|
"InvalidDispenseVolumeError",
|
|
166
172
|
"StorageLimitReachedError",
|
|
173
|
+
"LiquidClassDoesNotExistError",
|
|
174
|
+
"LiquidClassRedefinitionError",
|
|
167
175
|
]
|
|
@@ -4,7 +4,7 @@ from logging import getLogger
|
|
|
4
4
|
from datetime import datetime
|
|
5
5
|
from textwrap import dedent
|
|
6
6
|
from typing import Any, Dict, Mapping, List, Type, Union, Optional, Sequence
|
|
7
|
-
from pydantic import BaseModel, Field
|
|
7
|
+
from pydantic import BaseModel, Field, ConfigDict
|
|
8
8
|
from opentrons_shared_data.errors.codes import ErrorCodes
|
|
9
9
|
from .exceptions import ProtocolEngineError
|
|
10
10
|
from opentrons_shared_data.errors.exceptions import EnumeratedError
|
|
@@ -29,7 +29,7 @@ class ErrorOccurrence(BaseModel):
|
|
|
29
29
|
wrappedErrors = [
|
|
30
30
|
cls.from_failed(id, createdAt, err) for err in error.wrapping
|
|
31
31
|
]
|
|
32
|
-
return cls.
|
|
32
|
+
return cls.model_construct(
|
|
33
33
|
id=id,
|
|
34
34
|
createdAt=createdAt,
|
|
35
35
|
errorType=type(error).__name__,
|
|
@@ -39,6 +39,21 @@ class ErrorOccurrence(BaseModel):
|
|
|
39
39
|
wrappedErrors=wrappedErrors,
|
|
40
40
|
)
|
|
41
41
|
|
|
42
|
+
@staticmethod
|
|
43
|
+
def schema_extra(schema: Dict[str, Any], model: object) -> None:
|
|
44
|
+
"""Append the schema to make the errorCode appear required.
|
|
45
|
+
|
|
46
|
+
`errorCode`, `wrappedErrors`, and `errorInfo` have defaults because they are not included in earlier
|
|
47
|
+
versions of this model, _and_ this model is loaded directly from
|
|
48
|
+
the on-robot store. That means that, without a default, it will
|
|
49
|
+
fail to parse. Once a default is defined, the automated schema will
|
|
50
|
+
mark this as a non-required field, which is misleading as this is
|
|
51
|
+
a response from the server to the client and it will always have an
|
|
52
|
+
errorCode defined. This hack is required because it informs the client
|
|
53
|
+
that it does not, in fact, have to account for a missing errorCode, wrappedError, or errorInfo.
|
|
54
|
+
"""
|
|
55
|
+
schema["required"].extend(["errorCode", "wrappedErrors", "errorInfo"])
|
|
56
|
+
|
|
42
57
|
id: str = Field(..., description="Unique identifier of this error occurrence.")
|
|
43
58
|
createdAt: datetime = Field(..., description="When the error occurred.")
|
|
44
59
|
|
|
@@ -145,23 +160,7 @@ class ErrorOccurrence(BaseModel):
|
|
|
145
160
|
default=[], description="Errors that may have caused this one."
|
|
146
161
|
)
|
|
147
162
|
|
|
148
|
-
|
|
149
|
-
"""Customize configuration for this model."""
|
|
150
|
-
|
|
151
|
-
@staticmethod
|
|
152
|
-
def schema_extra(schema: Dict[str, Any], model: object) -> None:
|
|
153
|
-
"""Append the schema to make the errorCode appear required.
|
|
154
|
-
|
|
155
|
-
`errorCode`, `wrappedErrors`, and `errorInfo` have defaults because they are not included in earlier
|
|
156
|
-
versions of this model, _and_ this model is loaded directly from
|
|
157
|
-
the on-robot store. That means that, without a default, it will
|
|
158
|
-
fail to parse. Once a default is defined, the automated schema will
|
|
159
|
-
mark this as a non-required field, which is misleading as this is
|
|
160
|
-
a response from the server to the client and it will always have an
|
|
161
|
-
errorCode defined. This hack is required because it informs the client
|
|
162
|
-
that it does not, in fact, have to account for a missing errorCode, wrappedError, or errorInfo.
|
|
163
|
-
"""
|
|
164
|
-
schema["required"].extend(["errorCode", "wrappedErrors", "errorInfo"])
|
|
163
|
+
model_config = ConfigDict(json_schema_extra=schema_extra)
|
|
165
164
|
|
|
166
165
|
|
|
167
166
|
# TODO (tz, 7-12-23): move this to exceptions.py when we stop relaying on ErrorOccurrence.
|
|
@@ -180,4 +179,4 @@ class ProtocolCommandFailedError(ProtocolEngineError):
|
|
|
180
179
|
self.original_error = original_error
|
|
181
180
|
|
|
182
181
|
|
|
183
|
-
ErrorOccurrence.
|
|
182
|
+
ErrorOccurrence.model_rebuild()
|
|
@@ -244,6 +244,19 @@ class LiquidDoesNotExistError(ProtocolEngineError):
|
|
|
244
244
|
super().__init__(ErrorCodes.GENERAL_ERROR, message, details, wrapping)
|
|
245
245
|
|
|
246
246
|
|
|
247
|
+
class InvalidLiquidError(ProtocolEngineError):
|
|
248
|
+
"""Raised when attempting to add a liquid with an invalid property."""
|
|
249
|
+
|
|
250
|
+
def __init__(
|
|
251
|
+
self,
|
|
252
|
+
message: Optional[str] = None,
|
|
253
|
+
details: Optional[Dict[str, Any]] = None,
|
|
254
|
+
wrapping: Optional[Sequence[EnumeratedError]] = None,
|
|
255
|
+
) -> None:
|
|
256
|
+
"""Build an InvalidLiquidError."""
|
|
257
|
+
super().__init__(ErrorCodes.INVALID_PROTOCOL_DATA, message, details, wrapping)
|
|
258
|
+
|
|
259
|
+
|
|
247
260
|
class LabwareDefinitionDoesNotExistError(ProtocolEngineError):
|
|
248
261
|
"""Raised when referencing a labware definition that does not exist."""
|
|
249
262
|
|
|
@@ -348,6 +361,19 @@ class TouchTipDisabledError(ProtocolEngineError):
|
|
|
348
361
|
super().__init__(ErrorCodes.GENERAL_ERROR, message, details, wrapping)
|
|
349
362
|
|
|
350
363
|
|
|
364
|
+
class TouchTipIncompatibleArgumentsError(ProtocolEngineError):
|
|
365
|
+
"""Raised when touch tip is used with both a custom radius and a mmFromEdge argument."""
|
|
366
|
+
|
|
367
|
+
def __init__(
|
|
368
|
+
self,
|
|
369
|
+
message: Optional[str] = None,
|
|
370
|
+
details: Optional[Dict[str, Any]] = None,
|
|
371
|
+
wrapping: Optional[Sequence[EnumeratedError]] = None,
|
|
372
|
+
) -> None:
|
|
373
|
+
"""Build a TouchTipIncompatibleArgumentsError."""
|
|
374
|
+
super().__init__(ErrorCodes.GENERAL_ERROR, message, details, wrapping)
|
|
375
|
+
|
|
376
|
+
|
|
351
377
|
class WellDoesNotExistError(ProtocolEngineError):
|
|
352
378
|
"""Raised when referencing a well that does not exist."""
|
|
353
379
|
|
|
@@ -1155,3 +1181,27 @@ class StorageLimitReachedError(ProtocolEngineError):
|
|
|
1155
1181
|
) -> None:
|
|
1156
1182
|
"""Build an StorageLimitReached."""
|
|
1157
1183
|
super().__init__(ErrorCodes.GENERAL_ERROR, message, detail, wrapping)
|
|
1184
|
+
|
|
1185
|
+
|
|
1186
|
+
class LiquidClassDoesNotExistError(ProtocolEngineError):
|
|
1187
|
+
"""Raised when referencing a liquid class that has not been loaded."""
|
|
1188
|
+
|
|
1189
|
+
def __init__(
|
|
1190
|
+
self,
|
|
1191
|
+
message: Optional[str] = None,
|
|
1192
|
+
details: Optional[Dict[str, Any]] = None,
|
|
1193
|
+
wrapping: Optional[Sequence[EnumeratedError]] = None,
|
|
1194
|
+
) -> None:
|
|
1195
|
+
super().__init__(ErrorCodes.GENERAL_ERROR, message, details, wrapping)
|
|
1196
|
+
|
|
1197
|
+
|
|
1198
|
+
class LiquidClassRedefinitionError(ProtocolEngineError):
|
|
1199
|
+
"""Raised when attempting to load a liquid class that conflicts with a liquid class already loaded."""
|
|
1200
|
+
|
|
1201
|
+
def __init__(
|
|
1202
|
+
self,
|
|
1203
|
+
message: Optional[str] = None,
|
|
1204
|
+
details: Optional[Dict[str, Any]] = None,
|
|
1205
|
+
wrapping: Optional[Sequence[EnumeratedError]] = None,
|
|
1206
|
+
) -> None:
|
|
1207
|
+
super().__init__(ErrorCodes.GENERAL_ERROR, message, details, wrapping)
|
|
@@ -188,7 +188,7 @@ class CommandExecutor:
|
|
|
188
188
|
"completedAt": self._model_utils.get_timestamp(),
|
|
189
189
|
"notes": note_tracker.get_notes(),
|
|
190
190
|
}
|
|
191
|
-
succeeded_command = running_command.
|
|
191
|
+
succeeded_command = running_command.model_copy(update=update)
|
|
192
192
|
self._action_dispatcher.dispatch(
|
|
193
193
|
SucceedCommandAction(
|
|
194
194
|
command=succeeded_command,
|