From 59710a2beb0da765f4f259ef735414425f1a3976 Mon Sep 17 00:00:00 2001 From: tamarzanzouri Date: Thu, 14 Sep 2023 14:45:14 -0400 Subject: [PATCH 01/41] added schema definition for fixtures --- shared-data/fixture/schemas/1.json | 149 +++++++++++++++++++++++++++++ 1 file changed, 149 insertions(+) create mode 100644 shared-data/fixture/schemas/1.json diff --git a/shared-data/fixture/schemas/1.json b/shared-data/fixture/schemas/1.json new file mode 100644 index 00000000000..647d49a5ac3 --- /dev/null +++ b/shared-data/fixture/schemas/1.json @@ -0,0 +1,149 @@ +{ + "$id": "opentronsFixtureSchemaV1", + "$schema": "http://json-schema.org/draft-07/schema#", + "definitions": { + "positiveNumber": { + "type": "number", + "minimum": 0 + }, + "brandData": { + "type": "object", + "additionalProperties": false, + "required": ["brand"], + "properties": { + "brand": { + "type": "string", + "description": "Brand/manufacturer name" + }, + "brandId": { + "type": "array", + "description": "An array of manufacture numbers pertaining to a given labware", + "items": { + "type": "string" + } + }, + "links": { + "type": "array", + "description": "URLs for manufacturer page(s)", + "items": { + "type": "string" + } + } + } + }, + "displayCategory": { + "type": "string", + "enum": [ + "slot", + "extensionSlot", + "trash", + "other" + ] + }, + "safeString": { + "description": "a string safe to use for loadName / namespace. Lowercase-only.", + "type": "string", + "pattern": "^[a-z0-9._]+$" + }, + "coordinates": { + "type": "object", + "additionalProperties": false, + "required": ["x", "y", "z"], + "properties": { + "x": { + "type": "number" + }, + "y": { + "type": "number" + }, + "z": { + "type": "number" + } + } + } + }, + "type": "object", + "additionalProperties": false, + "required": [ + "schemaVersion", + "version", + "namespace", + "metadata", + "brand", + "parameters", + "dimensions" + ], + "properties": { + "schemaVersion": { + "description": "Which schema version a fixture is using", + "type": "number", + "enum": [2] + }, + "version": { + "description": "Version of the fixture definition itself (eg slot v1/v2/v3). An incrementing integer", + "type": "integer", + "minimum": 1 + }, + "namespace": { + "$ref": "#/definitions/safeString" + }, + "metadata": { + "type": "object", + "description": "Properties used for search and display", + "additionalProperties": false, + "required": ["displayName", "displayCategory"], + "properties": { + "displayName": { + "description": "Easy to remember name of labware", + "type": "string" + }, + "displayCategory": { + "$ref": "#/definitions/displayCategory", + "description": "Label(s) used in UI to categorize fixture" + }, + "tags": { + "type": "array", + "description": "List of descriptions for a given fixture", + "items": { + "type": "string" + } + } + } + }, + "brand": { + "$ref": "#/definitions/brandData", + "description": "Real-world fixfure that the definition is modeled from and/or compatible with" + }, + "parameters": { + "type": "object", + "description": "Internal describers for the fixture", + "additionalProperties": false, + "required": [ + "loadName" + ], + "properties": { + "loadName": { + "description": "Name used to reference a fixture definition", + "$ref": "#/definitions/safeString" + } + } + }, + "dimensions": { + "type": "object", + "additionalProperties": false, + "description": "Outer dimensions of a fixture", + "required": ["xDimension", "yDimension", "zDimension"], + "properties": { + "yDimension": { + "$ref": "#/definitions/positiveNumber" + }, + "zDimension": { + "$ref": "#/definitions/positiveNumber" + }, + "xDimension": { + "$ref": "#/definitions/positiveNumber" + } + } + } + } +} From 755abd3686fcad4c3fc5e7df61c96d9047e537ee Mon Sep 17 00:00:00 2001 From: tamarzanzouri Date: Thu, 4 Apr 2024 17:08:48 -0400 Subject: [PATCH 02/41] initial commit WIP --- api/src/opentrons/protocol_engine/commands/command.py | 1 + robot-server/robot_server/runs/router/commands_router.py | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/api/src/opentrons/protocol_engine/commands/command.py b/api/src/opentrons/protocol_engine/commands/command.py index 5c2ab46b06f..11d357339a2 100644 --- a/api/src/opentrons/protocol_engine/commands/command.py +++ b/api/src/opentrons/protocol_engine/commands/command.py @@ -55,6 +55,7 @@ class CommandIntent(str, Enum): PROTOCOL = "protocol" SETUP = "setup" + FIXIT = "fixit" class BaseCommandCreate(GenericModel, Generic[CommandParamsT]): diff --git a/robot-server/robot_server/runs/router/commands_router.py b/robot-server/robot_server/runs/router/commands_router.py index 734d1a26066..0fe85df4867 100644 --- a/robot-server/robot_server/runs/router/commands_router.py +++ b/robot-server/robot_server/runs/router/commands_router.py @@ -128,6 +128,7 @@ async def get_current_run_engine_from_url( - Setup commands (`data.source == "setup"`) - Protocol commands (`data.source == "protocol"`) + - Fixit commands (`data.source == "fixit"`) Setup commands may be enqueued before the run has been started. You could use setup commands to prepare a module or @@ -137,6 +138,11 @@ async def get_current_run_engine_from_url( You can create a protocol purely over HTTP using protocol commands. If you are running a protocol from a file(s), then you will likely not need to enqueue protocol commands using this endpoint. + + Fixit commands may be enqueued anytime using this endpoint. + These commands are intended to fix a failed command. + They will be executed right after the failed command + and only if the run is in a paused state. Once enqueued, setup commands will execute immediately with priority, while protocol commands will wait until a `play` action is issued. From 8fcbcee40cb8828270097e478e9a546211b01976 Mon Sep 17 00:00:00 2001 From: tamarzanzouri Date: Thu, 4 Apr 2024 21:29:37 -0400 Subject: [PATCH 03/41] WIP added fixit commands to action validation --- .../opentrons/protocol_engine/errors/__init__.py | 2 ++ .../opentrons/protocol_engine/errors/exceptions.py | 13 +++++++++++++ .../opentrons/protocol_engine/protocol_engine.py | 9 +++++---- .../opentrons/protocol_engine/state/commands.py | 8 ++++++++ .../protocol_engine/state/test_command_view_old.py | 14 ++++++++++++++ 5 files changed, 42 insertions(+), 4 deletions(-) diff --git a/api/src/opentrons/protocol_engine/errors/__init__.py b/api/src/opentrons/protocol_engine/errors/__init__.py index d3c3bb6d79e..92bdd36f3d9 100644 --- a/api/src/opentrons/protocol_engine/errors/__init__.py +++ b/api/src/opentrons/protocol_engine/errors/__init__.py @@ -39,6 +39,7 @@ MustHomeError, RunStoppedError, SetupCommandNotAllowedError, + FixitCommandNotAllowedError, ModuleNotAttachedError, ModuleAlreadyPresentError, WrongModuleTypeError, @@ -109,6 +110,7 @@ "MustHomeError", "RunStoppedError", "SetupCommandNotAllowedError", + "FixitCommandNotAllowedError", "ModuleNotAttachedError", "ModuleAlreadyPresentError", "WrongModuleTypeError", diff --git a/api/src/opentrons/protocol_engine/errors/exceptions.py b/api/src/opentrons/protocol_engine/errors/exceptions.py index 9d9ff99b33e..abf6ab9b775 100644 --- a/api/src/opentrons/protocol_engine/errors/exceptions.py +++ b/api/src/opentrons/protocol_engine/errors/exceptions.py @@ -505,6 +505,19 @@ def __init__( super().__init__(ErrorCodes.POSITION_UNKNOWN, message, details, wrapping) +class FixitCommandNotAllowedError(ProtocolEngineError): + """Raised when adding a fixit command to a non-recoverable engine.""" + + def __init__( + self, + message: Optional[str] = None, + details: Optional[Dict[str, Any]] = None, + wrapping: Optional[Sequence[EnumeratedError]] = None, + ) -> None: + """Build a SetupCommandNotAllowedError.""" + super().__init__(ErrorCodes.GENERAL_ERROR, message, details, wrapping) + + class SetupCommandNotAllowedError(ProtocolEngineError): """Raised when adding a setup command to a non-idle/non-paused engine.""" diff --git a/api/src/opentrons/protocol_engine/protocol_engine.py b/api/src/opentrons/protocol_engine/protocol_engine.py index 8e23c08013f..a97edf58593 100644 --- a/api/src/opentrons/protocol_engine/protocol_engine.py +++ b/api/src/opentrons/protocol_engine/protocol_engine.py @@ -195,10 +195,11 @@ def add_command(self, request: commands.CommandCreate) -> commands.Command: ) command_id = self._model_utils.generate_id() - request_hash = commands.hash_command_params( - create=request, - last_hash=self._state_store.commands.get_latest_command_hash(), - ) + if request.intent != commands.CommandIntent.FIXIT: + request_hash = commands.hash_command_params( + create=request, + last_hash=self._state_store.commands.get_latest_command_hash(), + ) action = self.state_view.commands.validate_action_allowed( QueueCommandAction( diff --git a/api/src/opentrons/protocol_engine/state/commands.py b/api/src/opentrons/protocol_engine/state/commands.py index 2c66e45826d..9139dd01b64 100644 --- a/api/src/opentrons/protocol_engine/state/commands.py +++ b/api/src/opentrons/protocol_engine/state/commands.py @@ -38,6 +38,7 @@ ErrorOccurrence, RobotDoorOpenError, SetupCommandNotAllowedError, + FixitCommandNotAllowedError, PauseNotAllowedError, UnexpectedProtocolError, ProtocolCommandFailedError, @@ -804,6 +805,13 @@ def validate_action_allowed( # noqa: C901 raise SetupCommandNotAllowedError( "Setup commands are not allowed after run has started." ) + elif ( + action.request.intent == CommandIntent.FIXIT + and self._state.queue_status != QueueStatus.AWAITING_RECOVERY + ): + raise FixitCommandNotAllowedError( + "Fixit commands are not allowed when the run is not in a recoverable state." + ) else: return action diff --git a/api/tests/opentrons/protocol_engine/state/test_command_view_old.py b/api/tests/opentrons/protocol_engine/state/test_command_view_old.py index 64d7670f662..0abc4f55739 100644 --- a/api/tests/opentrons/protocol_engine/state/test_command_view_old.py +++ b/api/tests/opentrons/protocol_engine/state/test_command_view_old.py @@ -491,6 +491,20 @@ class ActionAllowedSpec(NamedTuple): action=ResumeFromRecoveryAction(), expected_error=NotImplementedError, ), + # fixit command is disallowed if not in recovery mode + ActionAllowedSpec( + subject=get_command_view(queue_status=QueueStatus.RUNNING), + action=QueueCommandAction( + request=cmd.HomeCreate( + params=cmd.HomeParams(), + intent=cmd.CommandIntent.FIXIT, + ), + request_hash=None, + command_id="command-id", + created_at=datetime(year=2021, month=1, day=1), + ), + expected_error=errors.FixitCommandNotAllowedError, + ), ] From eb2caed4544e3333bfd22595627eed5962868fa0 Mon Sep 17 00:00:00 2001 From: tamarzanzouri Date: Mon, 8 Apr 2024 22:04:41 -0400 Subject: [PATCH 04/41] WIP commands_history now containing fixit queue and executed commands --- .../protocol_engine/commands/command.py | 6 +++++ .../protocol_engine/state/command_history.py | 24 +++++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/api/src/opentrons/protocol_engine/commands/command.py b/api/src/opentrons/protocol_engine/commands/command.py index 11d357339a2..eaa223f7b36 100644 --- a/api/src/opentrons/protocol_engine/commands/command.py +++ b/api/src/opentrons/protocol_engine/commands/command.py @@ -160,6 +160,12 @@ class BaseCommand(GenericModel, Generic[CommandParamsT, CommandResultT]): " the command's execution or the command's generation." ), ) + failed_command: Optional[BaseCommand] = Field( + None, + description=( + "FIXIT command use only. Reference of the failed command we are trying to fix." + ), + ) class AbstractCommandImpl( diff --git a/api/src/opentrons/protocol_engine/state/command_history.py b/api/src/opentrons/protocol_engine/state/command_history.py index 6a66a2b8209..b7906a9f182 100644 --- a/api/src/opentrons/protocol_engine/state/command_history.py +++ b/api/src/opentrons/protocol_engine/state/command_history.py @@ -33,6 +33,12 @@ class CommandHistory: _queued_setup_command_ids: OrderedSet[str] """The IDs of queued setup commands, in FIFO order""" + _queued_fixit_command_ids: OrderedSet[str] + """The IDs of queued fixit commands, in FIFO order""" + + _executed_command_ids: List[str] + """The IDs of all executed commands (setup, fixit, protocol) ordered from oldest to newest.""" + _running_command_id: Optional[str] """The ID of the currently running command, if any""" @@ -43,6 +49,8 @@ def __init__(self) -> None: self._all_command_ids = [] self._queued_command_ids = OrderedSet() self._queued_setup_command_ids = OrderedSet() + self._queued_fixit_command_ids = OrderedSet() + self._executed_command_ids = [] self._commands_by_id = OrderedDict() self._running_command_id = None self._terminal_command_id = None @@ -135,6 +143,10 @@ def get_setup_queue_ids(self) -> OrderedSet[str]: """Get the IDs of all queued setup commands, in FIFO order.""" return self._queued_setup_command_ids + def get_fixit_queue_ids(self) -> OrderedSet[str]: + """Get the IDs of all queued fixit commands, in FIFO order.""" + return self._queued_fixit_command_ids + def clear_queue(self) -> None: """Clears all commands within the queued command ids structure.""" self._queued_command_ids.clear() @@ -143,6 +155,10 @@ def clear_setup_queue(self) -> None: """Clears all commands within the queued setup command ids structure.""" self._queued_setup_command_ids.clear() + def clear_fixit_queue(self) -> None: + """Clears all commands within the queued setup command ids structure.""" + self._queued_fixit_command_ids.clear() + def set_command_queued(self, command: Command) -> None: """Validate and mark a command as queued in the command history.""" assert command.status == CommandStatus.QUEUED @@ -239,6 +255,10 @@ def _add_to_setup_queue(self, command_id: str) -> None: """Add a new ID to the queued setup.""" self._queued_setup_command_ids.add(command_id) + def _add_to_fixit_queue(self, command_id: str) -> None: + """Add a new ID to the queued fixit.""" + self._queued_fixit_command_ids.add(command_id) + def _remove_queue_id(self, command_id: str) -> None: """Remove a specific command from the queued command ids structure.""" self._queued_command_ids.discard(command_id) @@ -247,6 +267,10 @@ def _remove_setup_queue_id(self, command_id: str) -> None: """Remove a specific command from the queued setup command ids structure.""" self._queued_setup_command_ids.discard(command_id) + def _remove_fixit_queue_id(self, command_id: str) -> None: + """Remove a specific command from the queued fixit command ids structure.""" + self._queued_fixit_command_ids.discard(command_id) + def _set_terminal_command_id(self, command_id: str) -> None: """Set the ID of the most recently dequeued command.""" self._terminal_command_id = command_id From 3d1f734d73c08a535d0722138a7442f48f334e96 Mon Sep 17 00:00:00 2001 From: tamarzanzouri Date: Tue, 9 Apr 2024 11:37:55 -0400 Subject: [PATCH 05/41] added logic for checking failed command and adding to fixit queue --- .../protocol_engine/commands/command.py | 13 ++++++----- .../protocol_engine/state/command_history.py | 2 ++ .../protocol_engine/state/commands.py | 22 +++++++++++++------ 3 files changed, 24 insertions(+), 13 deletions(-) diff --git a/api/src/opentrons/protocol_engine/commands/command.py b/api/src/opentrons/protocol_engine/commands/command.py index eaa223f7b36..c68a10765d2 100644 --- a/api/src/opentrons/protocol_engine/commands/command.py +++ b/api/src/opentrons/protocol_engine/commands/command.py @@ -21,6 +21,7 @@ from opentrons.hardware_control import HardwareControlAPI from ..errors import ErrorOccurrence +from .command_unions import Command from ..notes import CommandNote, CommandNoteAdder # Work around type-only circular dependencies. @@ -95,6 +96,12 @@ class BaseCommandCreate(GenericModel, Generic[CommandParamsT]): " If a value is not provided, one will be generated." ), ) + failed_command: Optional[Command] = Field( + None, + description=( + "FIXIT command use only. Reference of the failed command we are trying to fix." + ), + ) class BaseCommand(GenericModel, Generic[CommandParamsT, CommandResultT]): @@ -160,12 +167,6 @@ class BaseCommand(GenericModel, Generic[CommandParamsT, CommandResultT]): " the command's execution or the command's generation." ), ) - failed_command: Optional[BaseCommand] = Field( - None, - description=( - "FIXIT command use only. Reference of the failed command we are trying to fix." - ), - ) class AbstractCommandImpl( diff --git a/api/src/opentrons/protocol_engine/state/command_history.py b/api/src/opentrons/protocol_engine/state/command_history.py index b7906a9f182..81669f97243 100644 --- a/api/src/opentrons/protocol_engine/state/command_history.py +++ b/api/src/opentrons/protocol_engine/state/command_history.py @@ -173,6 +173,8 @@ def set_command_queued(self, command: Command) -> None: if command.intent == CommandIntent.SETUP: self._add_to_setup_queue(command.id) + elif command.intent == CommandIntent.FIXIT: + self._add_to_fixit_queue(command.id) else: self._add_to_queue(command.id) diff --git a/api/src/opentrons/protocol_engine/state/commands.py b/api/src/opentrons/protocol_engine/state/commands.py index 9139dd01b64..a3c8bdaf637 100644 --- a/api/src/opentrons/protocol_engine/state/commands.py +++ b/api/src/opentrons/protocol_engine/state/commands.py @@ -805,13 +805,21 @@ def validate_action_allowed( # noqa: C901 raise SetupCommandNotAllowedError( "Setup commands are not allowed after run has started." ) - elif ( - action.request.intent == CommandIntent.FIXIT - and self._state.queue_status != QueueStatus.AWAITING_RECOVERY - ): - raise FixitCommandNotAllowedError( - "Fixit commands are not allowed when the run is not in a recoverable state." - ) + elif action.request.intent == CommandIntent.FIXIT: + if self._state.queue_status != QueueStatus.AWAITING_RECOVERY: + raise FixitCommandNotAllowedError( + "Fixit commands are not allowed when the run is not in a recoverable state." + ) + elif not self._state.failed_command or ( + self._state.failed_command + and self._state.failed_command.command + != action.request.failed_command + ): + raise FixitCommandNotAllowedError( + "Fixit command does not match failed command." + ) + else: + return action else: return action From b1bde235776e6c7ee1b011afd4f42904917539fe Mon Sep 17 00:00:00 2001 From: tamarzanzouri Date: Tue, 9 Apr 2024 15:00:55 -0400 Subject: [PATCH 06/41] fixed circular dependency and added test --- .../protocol_engine/commands/command.py | 5 ++-- .../protocol_engine/state/commands.py | 4 +-- .../state/test_command_view_old.py | 29 +++++++++++++++++++ 3 files changed, 33 insertions(+), 5 deletions(-) diff --git a/api/src/opentrons/protocol_engine/commands/command.py b/api/src/opentrons/protocol_engine/commands/command.py index c68a10765d2..84d3a938447 100644 --- a/api/src/opentrons/protocol_engine/commands/command.py +++ b/api/src/opentrons/protocol_engine/commands/command.py @@ -21,7 +21,6 @@ from opentrons.hardware_control import HardwareControlAPI from ..errors import ErrorOccurrence -from .command_unions import Command from ..notes import CommandNote, CommandNoteAdder # Work around type-only circular dependencies. @@ -96,10 +95,10 @@ class BaseCommandCreate(GenericModel, Generic[CommandParamsT]): " If a value is not provided, one will be generated." ), ) - failed_command: Optional[Command] = Field( + failed_command_id: Optional[str] = Field( None, description=( - "FIXIT command use only. Reference of the failed command we are trying to fix." + "FIXIT command use only. Reference of the failed command id we are trying to fix." ), ) diff --git a/api/src/opentrons/protocol_engine/state/commands.py b/api/src/opentrons/protocol_engine/state/commands.py index a3c8bdaf637..a2c95cc2b29 100644 --- a/api/src/opentrons/protocol_engine/state/commands.py +++ b/api/src/opentrons/protocol_engine/state/commands.py @@ -812,8 +812,8 @@ def validate_action_allowed( # noqa: C901 ) elif not self._state.failed_command or ( self._state.failed_command - and self._state.failed_command.command - != action.request.failed_command + and self._state.failed_command.command.id + != action.request.failed_command_id ): raise FixitCommandNotAllowedError( "Fixit command does not match failed command." diff --git a/api/tests/opentrons/protocol_engine/state/test_command_view_old.py b/api/tests/opentrons/protocol_engine/state/test_command_view_old.py index 0abc4f55739..cfb38104d51 100644 --- a/api/tests/opentrons/protocol_engine/state/test_command_view_old.py +++ b/api/tests/opentrons/protocol_engine/state/test_command_view_old.py @@ -505,6 +505,35 @@ class ActionAllowedSpec(NamedTuple): ), expected_error=errors.FixitCommandNotAllowedError, ), + ActionAllowedSpec( + subject=get_command_view( + queue_status=QueueStatus.AWAITING_RECOVERY, + failed_command=CommandEntry( + index=2, + command=create_failed_command( + command_id="command-id-3", + error=ErrorOccurrence( + id="error-id", + errorType="ProtocolEngineError", + createdAt=datetime(year=2022, month=2, day=2), + detail="oh no", + errorCode=ErrorCodes.GENERAL_ERROR.value.code, + ), + ), + ), + ), + action=QueueCommandAction( + request=cmd.HomeCreate( + params=cmd.HomeParams(), + intent=cmd.CommandIntent.FIXIT, + failed_command_id="command-id-2", + ), + request_hash=None, + command_id="command-id", + created_at=datetime(year=2021, month=1, day=1), + ), + expected_error=errors.FixitCommandNotAllowedError, + ), ] From 942f8f0da181ec3a0b9351bbf2f27d7bb47283fb Mon Sep 17 00:00:00 2001 From: tamarzanzouri Date: Tue, 9 Apr 2024 17:00:25 -0400 Subject: [PATCH 07/41] added tests --- .../state/test_command_history.py | 22 ++++++++++++++ .../state/test_command_view_old.py | 29 +++++++++++++++++++ 2 files changed, 51 insertions(+) diff --git a/api/tests/opentrons/protocol_engine/state/test_command_history.py b/api/tests/opentrons/protocol_engine/state/test_command_history.py index c6344141281..3a82a3d4c9c 100644 --- a/api/tests/opentrons/protocol_engine/state/test_command_history.py +++ b/api/tests/opentrons/protocol_engine/state/test_command_history.py @@ -161,6 +161,14 @@ def test_get_setup_queue_ids(command_history: CommandHistory) -> None: assert command_history.get_setup_queue_ids() == OrderedSet(["0", "1"]) +def test_get_fixit_queue_ids(command_history: CommandHistory) -> None: + """It should return the IDs of all commands in the setup queue.""" + assert command_history.get_fixit_queue_ids() == OrderedSet() + command_history._add_to_fixit_queue("0") + command_history._add_to_fixit_queue("1") + assert command_history.get_fixit_queue_ids() == OrderedSet(["0", "1"]) + + def test_set_command_entry(command_history: CommandHistory) -> None: """It should set the command entry for the given ID.""" command_entry = create_queued_command_entry() @@ -196,6 +204,12 @@ def test_add_to_setup_queue(command_history: CommandHistory) -> None: assert command_history.get_setup_queue_ids() == OrderedSet(["0"]) +def test_add_to_fixit_queue(command_history: CommandHistory) -> None: + """It should add the given ID to the setup queue.""" + command_history._add_to_fixit_queue("0") + assert command_history.get_fixit_queue_ids() == OrderedSet(["0"]) + + def test_clear_queue(command_history: CommandHistory) -> None: """It should clear all commands in the queue.""" command_history._add_to_queue("0") @@ -212,6 +226,14 @@ def test_clear_setup_queue(command_history: CommandHistory) -> None: assert command_history.get_setup_queue_ids() == OrderedSet() +def test_clear_fixit_queue(command_history: CommandHistory) -> None: + """It should clear all commands in the setup queue.""" + command_history._add_to_fixit_queue("0") + command_history._add_to_fixit_queue("1") + command_history.clear_fixit_queue() + assert command_history.get_fixit_queue_ids() == OrderedSet() + + def test_remove_id_from_queue(command_history: CommandHistory) -> None: """It should remove the given ID from the queue.""" command_history._add_to_queue("0") diff --git a/api/tests/opentrons/protocol_engine/state/test_command_view_old.py b/api/tests/opentrons/protocol_engine/state/test_command_view_old.py index cfb38104d51..957fe0e3fe3 100644 --- a/api/tests/opentrons/protocol_engine/state/test_command_view_old.py +++ b/api/tests/opentrons/protocol_engine/state/test_command_view_old.py @@ -534,6 +534,35 @@ class ActionAllowedSpec(NamedTuple): ), expected_error=errors.FixitCommandNotAllowedError, ), + ActionAllowedSpec( + subject=get_command_view( + queue_status=QueueStatus.AWAITING_RECOVERY, + failed_command=CommandEntry( + index=2, + command=create_failed_command( + command_id="command-id-3", + error=ErrorOccurrence( + id="error-id", + errorType="ProtocolEngineError", + createdAt=datetime(year=2022, month=2, day=2), + detail="oh no", + errorCode=ErrorCodes.GENERAL_ERROR.value.code, + ), + ), + ), + ), + action=QueueCommandAction( + request=cmd.HomeCreate( + params=cmd.HomeParams(), + intent=cmd.CommandIntent.FIXIT, + failed_command_id="command-id-3", + ), + request_hash=None, + command_id="command-id", + created_at=datetime(year=2021, month=1, day=1), + ), + expected_error=None, + ), ] From 04bb500ab3de4bbeb39272a05ec07358db26fe14 Mon Sep 17 00:00:00 2001 From: tamarzanzouri Date: Tue, 9 Apr 2024 17:03:47 -0400 Subject: [PATCH 08/41] wip next command to execute --- api/src/opentrons/protocol_engine/state/commands.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/api/src/opentrons/protocol_engine/state/commands.py b/api/src/opentrons/protocol_engine/state/commands.py index a2c95cc2b29..757dbda29a2 100644 --- a/api/src/opentrons/protocol_engine/state/commands.py +++ b/api/src/opentrons/protocol_engine/state/commands.py @@ -599,6 +599,10 @@ def get_next_to_execute(self) -> Optional[str]: if self._state.run_result: raise RunStoppedError("Engine was stopped") + # if queue is in recovery mode, return the next fixit command + if self._state.queue_status == QueueStatus.AWAITING_RECOVERY: + return self._state.command_history.get_fixit_queue_ids().head(None) + # if there is a setup command queued, prioritize it next_setup_cmd = self._state.command_history.get_setup_queue_ids().head(None) if self._state.queue_status != QueueStatus.PAUSED and next_setup_cmd: From d28fe8d4e87e7728f6efa8b50466683004fc5e97 Mon Sep 17 00:00:00 2001 From: tamarzanzouri Date: Tue, 9 Apr 2024 21:37:24 -0400 Subject: [PATCH 09/41] get next queued prioritized fixit --- .../protocol_engine/state/command_history.py | 5 +--- .../protocol_engine/state/commands.py | 5 ++-- .../protocol_engine/state/command_fixtures.py | 2 ++ .../state/test_command_history.py | 25 ++++++++++++++++++ .../state/test_command_view_old.py | 26 ++++++++++++++++++- 5 files changed, 56 insertions(+), 7 deletions(-) diff --git a/api/src/opentrons/protocol_engine/state/command_history.py b/api/src/opentrons/protocol_engine/state/command_history.py index 81669f97243..b21fca030ae 100644 --- a/api/src/opentrons/protocol_engine/state/command_history.py +++ b/api/src/opentrons/protocol_engine/state/command_history.py @@ -36,9 +36,6 @@ class CommandHistory: _queued_fixit_command_ids: OrderedSet[str] """The IDs of queued fixit commands, in FIFO order""" - _executed_command_ids: List[str] - """The IDs of all executed commands (setup, fixit, protocol) ordered from oldest to newest.""" - _running_command_id: Optional[str] """The ID of the currently running command, if any""" @@ -50,7 +47,6 @@ def __init__(self) -> None: self._queued_command_ids = OrderedSet() self._queued_setup_command_ids = OrderedSet() self._queued_fixit_command_ids = OrderedSet() - self._executed_command_ids = [] self._commands_by_id = OrderedDict() self._running_command_id = None self._terminal_command_id = None @@ -195,6 +191,7 @@ def set_command_running(self, command: Command) -> None: self._remove_queue_id(command.id) self._remove_setup_queue_id(command.id) + self._remove_fixit_queue_id(command.id) def set_command_succeeded(self, command: Command) -> None: """Validate and mark a command as succeeded in the command history.""" diff --git a/api/src/opentrons/protocol_engine/state/commands.py b/api/src/opentrons/protocol_engine/state/commands.py index 757dbda29a2..f2231c7d0dd 100644 --- a/api/src/opentrons/protocol_engine/state/commands.py +++ b/api/src/opentrons/protocol_engine/state/commands.py @@ -600,8 +600,9 @@ def get_next_to_execute(self) -> Optional[str]: raise RunStoppedError("Engine was stopped") # if queue is in recovery mode, return the next fixit command - if self._state.queue_status == QueueStatus.AWAITING_RECOVERY: - return self._state.command_history.get_fixit_queue_ids().head(None) + next_fixit_cmd = self._state.command_history.get_fixit_queue_ids().head(None) + if next_fixit_cmd and self._state.queue_status == QueueStatus.AWAITING_RECOVERY: + return next_fixit_cmd # if there is a setup command queued, prioritize it next_setup_cmd = self._state.command_history.get_setup_queue_ids().head(None) diff --git a/api/tests/opentrons/protocol_engine/state/command_fixtures.py b/api/tests/opentrons/protocol_engine/state/command_fixtures.py index 191dd49bd48..b8b47648b3a 100644 --- a/api/tests/opentrons/protocol_engine/state/command_fixtures.py +++ b/api/tests/opentrons/protocol_engine/state/command_fixtures.py @@ -24,6 +24,7 @@ def create_queued_command( command_id: str = "command-id", command_key: str = "command-key", command_type: str = "command-type", + intent: cmd.CommandIntent = cmd.CommandIntent.PROTOCOL, params: Optional[BaseModel] = None, ) -> cmd.Command: """Given command data, build a pending command model.""" @@ -36,6 +37,7 @@ def create_queued_command( createdAt=datetime(year=2021, month=1, day=1), status=cmd.CommandStatus.QUEUED, params=params or BaseModel(), + intent=intent, ), ) diff --git a/api/tests/opentrons/protocol_engine/state/test_command_history.py b/api/tests/opentrons/protocol_engine/state/test_command_history.py index 3a82a3d4c9c..cacf5fa39cb 100644 --- a/api/tests/opentrons/protocol_engine/state/test_command_history.py +++ b/api/tests/opentrons/protocol_engine/state/test_command_history.py @@ -5,6 +5,7 @@ from opentrons.protocol_engine.errors.exceptions import CommandDoesNotExistError from opentrons.protocol_engine.state.command_history import CommandHistory, CommandEntry +from opentrons.protocol_engine.commands import CommandIntent from .command_fixtures import ( create_queued_command, @@ -18,6 +19,15 @@ def create_queued_command_entry( return CommandEntry(create_queued_command(command_id=command_id), index) +def create_fixit_command_entry( + command_id: str = "command-id", index: int = 0 +) -> CommandEntry: + """Create a command entry for a fixit command.""" + return CommandEntry( + create_queued_command(command_id=command_id, intent=CommandIntent.FIXIT), index + ) + + @pytest.fixture def command_history() -> CommandHistory: """Instantiates a CommandHistory instance.""" @@ -192,6 +202,21 @@ def test_set_running_command_id(command_history: CommandHistory) -> None: assert command_history.get_running_command() == command_entry +def test_set_fixit_running_command_id(command_history: CommandHistory) -> None: + """It should set the ID of the currently running fixit command.""" + command_entry = create_queued_command_entry() + command_history._add("0", command_entry) + command_history._set_running_command_id("0") + fixit_command_entry = create_fixit_command_entry() + command_history._add("1", fixit_command_entry) + command_history._set_running_command_id("1") + assert command_history.get_running_command() == fixit_command_entry + assert command_history.get_all_commands() == [ + command_entry.command, + fixit_command_entry.command, + ] + + def test_add_to_queue(command_history: CommandHistory) -> None: """It should add the given ID to the queue.""" command_history._add_to_queue("0") diff --git a/api/tests/opentrons/protocol_engine/state/test_command_view_old.py b/api/tests/opentrons/protocol_engine/state/test_command_view_old.py index 957fe0e3fe3..e336db563be 100644 --- a/api/tests/opentrons/protocol_engine/state/test_command_view_old.py +++ b/api/tests/opentrons/protocol_engine/state/test_command_view_old.py @@ -55,6 +55,7 @@ def get_command_view( running_command_id: Optional[str] = None, queued_command_ids: Sequence[str] = (), queued_setup_command_ids: Sequence[str] = (), + queued_fixit_command_ids: Sequence[str] = (), run_error: Optional[errors.ErrorOccurrence] = None, failed_command: Optional[CommandEntry] = None, command_error_recovery_types: Optional[Dict[str, ErrorRecoveryType]] = None, @@ -73,6 +74,9 @@ def get_command_view( if queued_setup_command_ids: for command_id in queued_setup_command_ids: command_history._add_to_setup_queue(command_id) + if queued_fixit_command_ids: + for command_id in queued_fixit_command_ids: + command_history._add_to_fixit_queue(command_id) if commands: for index, command in enumerate(commands): command_history._add( @@ -131,6 +135,7 @@ def test_get_next_to_execute_returns_first_queued() -> None: subject = get_command_view( queue_status=QueueStatus.RUNNING, queued_command_ids=["command-id-1", "command-id-2"], + queued_fixit_command_ids=["fixit-id-1", "fixit-id-2"], ) assert subject.get_next_to_execute() == "command-id-1" @@ -138,7 +143,7 @@ def test_get_next_to_execute_returns_first_queued() -> None: @pytest.mark.parametrize( "queue_status", - [QueueStatus.SETUP, QueueStatus.RUNNING], + [QueueStatus.SETUP, QueueStatus.RUNNING, QueueStatus.AWAITING_RECOVERY], ) def test_get_next_to_execute_prioritizes_setup_command_queue( queue_status: QueueStatus, @@ -153,6 +158,24 @@ def test_get_next_to_execute_prioritizes_setup_command_queue( assert subject.get_next_to_execute() == "setup-command-id" +@pytest.mark.parametrize( + "queue_status", + [QueueStatus.AWAITING_RECOVERY], +) +def test_get_next_to_execute_prioritizes_fixit_command_queue( + queue_status: QueueStatus, +) -> None: + """It should prioritize fixit command queue over protocol command queue.""" + subject = get_command_view( + queue_status=queue_status, + queued_command_ids=["command-id-1", "command-id-2"], + queued_setup_command_ids=["setup-command-id"], + queued_fixit_command_ids=["fixit-1", "fixit-2"], + ) + + assert subject.get_next_to_execute() == "fixit-1" + + def test_get_next_to_execute_returns_none_when_no_queued() -> None: """It should return None if there are no queued commands.""" subject = get_command_view( @@ -184,6 +207,7 @@ def test_get_next_to_execute_returns_no_commands_if_paused() -> None: queue_status=QueueStatus.PAUSED, queued_setup_command_ids=["setup-id-1", "setup-id-2"], queued_command_ids=["command-id-1", "command-id-2"], + queued_fixit_command_ids=["fixit-id-1", "fixit-id-2"], ) result = subject.get_next_to_execute() From e8d2fc026102f6dc2d958a8d2951e4045227b576 Mon Sep 17 00:00:00 2001 From: tamarzanzouri Date: Tue, 9 Apr 2024 21:46:31 -0400 Subject: [PATCH 10/41] WIP e2e test for fixit recovery --- ...json_v6_run_failure_with_fixit.tavern.yaml | 124 ++++++++++++++++++ 1 file changed, 124 insertions(+) create mode 100644 robot-server/tests/integration/http_api/runs/test_json_v6_run_failure_with_fixit.tavern.yaml diff --git a/robot-server/tests/integration/http_api/runs/test_json_v6_run_failure_with_fixit.tavern.yaml b/robot-server/tests/integration/http_api/runs/test_json_v6_run_failure_with_fixit.tavern.yaml new file mode 100644 index 00000000000..4365811aedc --- /dev/null +++ b/robot-server/tests/integration/http_api/runs/test_json_v6_run_failure_with_fixit.tavern.yaml @@ -0,0 +1,124 @@ +test_name: Upload and run a JSON v6 protocol that should fail. + +marks: + - usefixtures: + - ot2_server_base_url +stages: + - name: Upload a protocol + request: + url: '{ot2_server_base_url}/protocols' + method: POST + files: + files: 'tests/integration/protocols/simple_v6_failure.json' + response: + status_code: 201 + save: + json: + protocol_id: data.id + + - name: Create run from protocol + request: + url: '{ot2_server_base_url}/runs' + method: POST + json: + data: + protocolId: '{protocol_id}' + response: + status_code: 201 + save: + json: + run_id: data.id + + - name: Play the run + request: + url: '{ot2_server_base_url}/runs/{run_id}/actions' + method: POST + json: + data: + actionType: play + response: + status_code: 201 + strict: + - json:off + + - name: Wait for the run to fail + max_retries: 10 + delay_after: 0.1 + request: + url: '{ot2_server_base_url}/runs/{run_id}' + method: GET + response: + status_code: 200 + strict: + - json:off + json: + data: + status: failed + + - name: Verify the run contains the expected error + request: + url: '{ot2_server_base_url}/runs/{run_id}' + method: GET + response: + status_code: 200 + strict: + - json:off + json: + data: + status: 'awaiting-recovery' + errors: + - id: !anystr + createdAt: !anystr + errorCode: '3005' + errorType: TipNotAttachedError + detail: Pipette should have a tip attached, but does not. + errorInfo: !anydict + wrappedErrors: !anylist + - name: Verify commands contain the expected results + request: + url: '{ot2_server_base_url}/runs/{run_id}/commands' + method: GET + response: + status_code: 200 + json: + links: + current: + href: !anystr + meta: + runId: !anystr + commandId: !anystr + index: 3 + key: !anystr + createdAt: !anystr + meta: + cursor: 3 + totalLength: 4 + data: + - id: !anystr + key: !anystr + commandType: aspirate + createdAt: !anystr + startedAt: !anystr + completedAt: !anystr + status: failed + notes: [] + error: + id: !anystr + errorType: TipNotAttachedError + createdAt: !anystr + detail: Pipette should have a tip attached, but does not. + errorCode: '3005' + errorInfo: !anydict + wrappedErrors: !anylist + params: + pipetteId: pipetteId + labwareId: tipRackId + wellName: A1 + wellLocation: + origin: bottom + offset: + x: 0 + y: 0 + z: 1 + flowRate: 3.78 + volume: 100 \ No newline at end of file From e059aef875ec13dbcb766ee4c6b65f1ec2ea8967 Mon Sep 17 00:00:00 2001 From: tamarzanzouri Date: Fri, 12 Apr 2024 12:31:28 -0400 Subject: [PATCH 11/41] WIP resume from recovery --- .../protocol_engine/actions/__init__.py | 2 + .../protocol_engine/commands/pick_up_tip.py | 3 + .../protocol_engine/errors/__init__.py | 2 + .../protocol_engine/errors/exceptions.py | 13 +++ .../protocol_engine/state/commands.py | 13 ++- .../state/test_command_store_old.py | 100 ++++++++++++++++++ .../state/test_command_view_old.py | 22 +++- 7 files changed, 153 insertions(+), 2 deletions(-) diff --git a/api/src/opentrons/protocol_engine/actions/__init__.py b/api/src/opentrons/protocol_engine/actions/__init__.py index b1181e6a50e..efe8ba63ada 100644 --- a/api/src/opentrons/protocol_engine/actions/__init__.py +++ b/api/src/opentrons/protocol_engine/actions/__init__.py @@ -26,6 +26,7 @@ DoorChangeAction, ResetTipsAction, SetPipetteMovementSpeedAction, + ResumeFromRecoveryAction ) __all__ = [ @@ -52,6 +53,7 @@ "DoorChangeAction", "ResetTipsAction", "SetPipetteMovementSpeedAction", + "ResumeFromRecoveryAction", # action payload values "PauseSource", "FinishErrorDetails", diff --git a/api/src/opentrons/protocol_engine/commands/pick_up_tip.py b/api/src/opentrons/protocol_engine/commands/pick_up_tip.py index 8c2902a5f4b..aa0f3e27877 100644 --- a/api/src/opentrons/protocol_engine/commands/pick_up_tip.py +++ b/api/src/opentrons/protocol_engine/commands/pick_up_tip.py @@ -5,6 +5,7 @@ from typing_extensions import Literal from ..types import DeckPoint +from ..errors import TipNotAttachedError from .pipetting_common import ( PipetteIdMixin, WellLocationMixin, @@ -83,6 +84,8 @@ async def execute(self, params: PickUpTipParams) -> PickUpTipResult: well_name=well_name, ) + raise TipNotAttachedError() + return PickUpTipResult( tipVolume=tip_geometry.volume, tipLength=tip_geometry.length, diff --git a/api/src/opentrons/protocol_engine/errors/__init__.py b/api/src/opentrons/protocol_engine/errors/__init__.py index 92bdd36f3d9..f36195cbd9d 100644 --- a/api/src/opentrons/protocol_engine/errors/__init__.py +++ b/api/src/opentrons/protocol_engine/errors/__init__.py @@ -56,6 +56,7 @@ InvalidHoldTimeError, CannotPerformModuleAction, PauseNotAllowedError, + ResumeForRecoveryNotAllowedError, GripperNotAttachedError, CannotPerformGripperAction, HardwareNotSupportedError, @@ -126,6 +127,7 @@ "InvalidBlockVolumeError", "InvalidHoldTimeError", "CannotPerformModuleAction", + "ResumeForRecoveryNotAllowedError", "PauseNotAllowedError", "ProtocolCommandFailedError", "GripperNotAttachedError", diff --git a/api/src/opentrons/protocol_engine/errors/exceptions.py b/api/src/opentrons/protocol_engine/errors/exceptions.py index abf6ab9b775..1889408eaa1 100644 --- a/api/src/opentrons/protocol_engine/errors/exceptions.py +++ b/api/src/opentrons/protocol_engine/errors/exceptions.py @@ -531,6 +531,19 @@ def __init__( super().__init__(ErrorCodes.GENERAL_ERROR, message, details, wrapping) +class ResumeForRecoveryNotAllowedError(ProtocolEngineError): + """Raised when attempting to resume a run from recovery that has a fixit command in the queue.""" + + def __init__( + self, + message: Optional[str] = None, + details: Optional[Dict[str, Any]] = None, + wrapping: Optional[Sequence[EnumeratedError]] = None, + ) -> None: + """Build a ResumeForRecoveryNotAllowedError.""" + super().__init__(ErrorCodes.GENERAL_ERROR, message, details, wrapping) + + class PauseNotAllowedError(ProtocolEngineError): """Raised when attempting to pause a run that is not running.""" diff --git a/api/src/opentrons/protocol_engine/state/commands.py b/api/src/opentrons/protocol_engine/state/commands.py index f2231c7d0dd..821b092e628 100644 --- a/api/src/opentrons/protocol_engine/state/commands.py +++ b/api/src/opentrons/protocol_engine/state/commands.py @@ -39,6 +39,7 @@ RobotDoorOpenError, SetupCommandNotAllowedError, FixitCommandNotAllowedError, + ResumeForRecoveryNotAllowedError, PauseNotAllowedError, UnexpectedProtocolError, ProtocolCommandFailedError, @@ -335,6 +336,7 @@ def handle_action(self, action: Action) -> None: # noqa: C901 self._state.queue_status = QueueStatus.PAUSED elif isinstance(action, ResumeFromRecoveryAction): + self._state.command_history.clear_fixit_queue() self._state.queue_status = QueueStatus.RUNNING elif isinstance(action, StopAction): @@ -830,7 +832,16 @@ def validate_action_allowed( # noqa: C901 elif isinstance(action, ResumeFromRecoveryAction): if self.get_status() != EngineStatus.AWAITING_RECOVERY: - raise NotImplementedError() + raise ResumeForRecoveryNotAllowedError( + "Cannot resume from recovery if the run is not in recovery mode." + ) + elif ( + self.get_status() == EngineStatus.AWAITING_RECOVERY + and len(self._state.command_history.get_fixit_queue_ids()) > 0 + ): + raise ResumeForRecoveryNotAllowedError( + "Cannot resume from recovery while there are fixit commands in the queue." + ) else: return action diff --git a/api/tests/opentrons/protocol_engine/state/test_command_store_old.py b/api/tests/opentrons/protocol_engine/state/test_command_store_old.py index 7afde4a6e4b..92ed40fb18b 100644 --- a/api/tests/opentrons/protocol_engine/state/test_command_store_old.py +++ b/api/tests/opentrons/protocol_engine/state/test_command_store_old.py @@ -42,6 +42,7 @@ StopAction, HardwareStoppedAction, DoorChangeAction, + ResumeFromRecoveryAction ) from opentrons.protocol_engine.state.command_history import CommandHistory @@ -1329,6 +1330,105 @@ def test_command_store_handles_command_failed() -> None: assert subject.state.command_history.get("command-id") == failed_command_entry +def test_command_store_handles_resume_from_recovery() -> None: + """It should resume and clear fixit commands queue.""" + error_recovery_type = ErrorRecoveryType.WAIT_FOR_RECOVERY + + expected_error_occurrence = errors.ErrorOccurrence( + id="error-id", + errorType="ProtocolEngineError", + createdAt=datetime(year=2023, month=3, day=3), + detail="oh no", + errorCode=ErrorCodes.GENERAL_ERROR.value.code, + ) + + expected_failed_command = commands.Comment( + id="command-id", + commandType="comment", + key="command-key", + createdAt=datetime(year=2021, month=1, day=1), + startedAt=datetime(year=2022, month=2, day=2), + completedAt=expected_error_occurrence.createdAt, + status=commands.CommandStatus.FAILED, + params=commands.CommentParams(message="hello, world"), + result=None, + error=expected_error_occurrence, + notes=[ + CommandNote( + noteKind="noteKind", + shortMessage="shortMessage", + longMessage="longMessage", + source="source", + ) + ], + ) + + subject = CommandStore(is_door_open=False, config=_make_config()) + + subject.handle_action( + QueueCommandAction( + command_id=expected_failed_command.id, + created_at=expected_failed_command.createdAt, + request=commands.CommentCreate( + params=expected_failed_command.params, key=expected_failed_command.key + ), + request_hash=None, + ) + ) + subject.handle_action( + RunCommandAction( + command_id=expected_failed_command.id, + # Ignore arg-type errors because we know this isn't None. + started_at=expected_failed_command.startedAt, # type: ignore[arg-type] + ) + ) + subject.handle_action( + FailCommandAction( + command_id=expected_failed_command.id, + error_id=expected_error_occurrence.id, + failed_at=expected_error_occurrence.createdAt, + error=errors.ProtocolEngineError(message="oh no"), + notes=[ + CommandNote( + noteKind="noteKind", + shortMessage="shortMessage", + longMessage="longMessage", + source="source", + ) + ], + type=error_recovery_type, + ) + ) + + failed_command_entry = CommandEntry(index=0, command=expected_failed_command) + command_history = CommandHistory() + command_history._add("command-id", failed_command_entry) + command_history._set_terminal_command_id("command-id") + command_history._add_to_fixit_queue("fixit-id-1") + command_history._add_to_fixit_queue("fixit-id-2") + + subject.handle_action( + ResumeFromRecoveryAction() + ) + + assert subject.state == CommandState( + command_history=command_history, + queue_status=QueueStatus.RUNNING, + run_result=None, + run_completed_at=None, + is_door_blocking=False, + run_error=None, + finish_error=None, + failed_command=failed_command_entry, + command_error_recovery_types={expected_failed_command.id: error_recovery_type}, + run_started_at=None, + latest_command_hash=None, + stopped_by_estop=False, + ) + + assert subject.state.command_history.get_fixit_queue_ids() == OrderedSet() + + def test_handles_hardware_stopped() -> None: """It should mark the hardware as stopped on HardwareStoppedAction.""" subject = CommandStore(is_door_open=False, config=_make_config()) diff --git a/api/tests/opentrons/protocol_engine/state/test_command_view_old.py b/api/tests/opentrons/protocol_engine/state/test_command_view_old.py index e336db563be..75ee4efe3d5 100644 --- a/api/tests/opentrons/protocol_engine/state/test_command_view_old.py +++ b/api/tests/opentrons/protocol_engine/state/test_command_view_old.py @@ -587,9 +587,29 @@ class ActionAllowedSpec(NamedTuple): ), expected_error=None, ), + ActionAllowedSpec( + subject=get_command_view( + queue_status=QueueStatus.AWAITING_RECOVERY, + queued_fixit_command_ids=["fixit-id-1", "fixit-id-2"], + failed_command=CommandEntry( + index=2, + command=create_failed_command( + command_id="command-id-3", + error=ErrorOccurrence( + id="error-id", + errorType="ProtocolEngineError", + createdAt=datetime(year=2022, month=2, day=2), + detail="oh no", + errorCode=ErrorCodes.GENERAL_ERROR.value.code, + ), + ), + ), + ), + action=ResumeFromRecoveryAction(), + expected_error=None, + ) ] - @pytest.mark.parametrize(ActionAllowedSpec._fields, action_allowed_specs) def test_validate_action_allowed( subject: CommandView, From 95171471186af669a431da6404ab9177f5aaf695 Mon Sep 17 00:00:00 2001 From: tamarzanzouri Date: Mon, 15 Apr 2024 16:13:28 -0400 Subject: [PATCH 12/41] WIP pr feedback - move failed_command_id to base command ad route and merge fixes --- .../protocol_engine/actions/__init__.py | 2 +- .../protocol_engine/commands/command.py | 12 ++++++------ .../opentrons/protocol_engine/errors/__init__.py | 4 ++-- .../protocol_engine/errors/exceptions.py | 4 ++-- .../opentrons/protocol_engine/state/commands.py | 14 +++----------- .../state/test_command_view_old.py | 5 ++--- .../robot_server/runs/router/commands_router.py | 16 ++++++++++++---- 7 files changed, 28 insertions(+), 29 deletions(-) diff --git a/api/src/opentrons/protocol_engine/actions/__init__.py b/api/src/opentrons/protocol_engine/actions/__init__.py index 15c9885e216..4fd55f308b9 100644 --- a/api/src/opentrons/protocol_engine/actions/__init__.py +++ b/api/src/opentrons/protocol_engine/actions/__init__.py @@ -27,7 +27,7 @@ DoorChangeAction, ResetTipsAction, SetPipetteMovementSpeedAction, - ResumeFromRecoveryAction + ResumeFromRecoveryAction, ) __all__ = [ diff --git a/api/src/opentrons/protocol_engine/commands/command.py b/api/src/opentrons/protocol_engine/commands/command.py index 84d3a938447..058e9eca275 100644 --- a/api/src/opentrons/protocol_engine/commands/command.py +++ b/api/src/opentrons/protocol_engine/commands/command.py @@ -95,12 +95,6 @@ class BaseCommandCreate(GenericModel, Generic[CommandParamsT]): " If a value is not provided, one will be generated." ), ) - failed_command_id: Optional[str] = Field( - None, - description=( - "FIXIT command use only. Reference of the failed command id we are trying to fix." - ), - ) class BaseCommand(GenericModel, Generic[CommandParamsT, CommandResultT]): @@ -166,6 +160,12 @@ class BaseCommand(GenericModel, Generic[CommandParamsT, CommandResultT]): " the command's execution or the command's generation." ), ) + failed_command_id: Optional[str] = Field( + None, + description=( + "FIXIT command use only. Reference of the failed command id we are trying to fix." + ), + ) class AbstractCommandImpl( diff --git a/api/src/opentrons/protocol_engine/errors/__init__.py b/api/src/opentrons/protocol_engine/errors/__init__.py index f36195cbd9d..a255b08b49d 100644 --- a/api/src/opentrons/protocol_engine/errors/__init__.py +++ b/api/src/opentrons/protocol_engine/errors/__init__.py @@ -56,7 +56,7 @@ InvalidHoldTimeError, CannotPerformModuleAction, PauseNotAllowedError, - ResumeForRecoveryNotAllowedError, + ResumeFromRecoveryNotAllowedError, GripperNotAttachedError, CannotPerformGripperAction, HardwareNotSupportedError, @@ -127,7 +127,7 @@ "InvalidBlockVolumeError", "InvalidHoldTimeError", "CannotPerformModuleAction", - "ResumeForRecoveryNotAllowedError", + "ResumeFromRecoveryNotAllowedError", "PauseNotAllowedError", "ProtocolCommandFailedError", "GripperNotAttachedError", diff --git a/api/src/opentrons/protocol_engine/errors/exceptions.py b/api/src/opentrons/protocol_engine/errors/exceptions.py index 1889408eaa1..99c3166b8c7 100644 --- a/api/src/opentrons/protocol_engine/errors/exceptions.py +++ b/api/src/opentrons/protocol_engine/errors/exceptions.py @@ -531,7 +531,7 @@ def __init__( super().__init__(ErrorCodes.GENERAL_ERROR, message, details, wrapping) -class ResumeForRecoveryNotAllowedError(ProtocolEngineError): +class ResumeFromRecoveryNotAllowedError(ProtocolEngineError): """Raised when attempting to resume a run from recovery that has a fixit command in the queue.""" def __init__( @@ -540,7 +540,7 @@ def __init__( details: Optional[Dict[str, Any]] = None, wrapping: Optional[Sequence[EnumeratedError]] = None, ) -> None: - """Build a ResumeForRecoveryNotAllowedError.""" + """Build a ResumeFromRecoveryNotAllowedError.""" super().__init__(ErrorCodes.GENERAL_ERROR, message, details, wrapping) diff --git a/api/src/opentrons/protocol_engine/state/commands.py b/api/src/opentrons/protocol_engine/state/commands.py index 22fe97c668c..b7c8bae4611 100644 --- a/api/src/opentrons/protocol_engine/state/commands.py +++ b/api/src/opentrons/protocol_engine/state/commands.py @@ -39,7 +39,7 @@ RobotDoorOpenError, SetupCommandNotAllowedError, FixitCommandNotAllowedError, - ResumeForRecoveryNotAllowedError, + ResumeFromRecoveryNotAllowedError, PauseNotAllowedError, UnexpectedProtocolError, ProtocolCommandFailedError, @@ -827,14 +827,6 @@ def validate_action_allowed( # noqa: C901 raise FixitCommandNotAllowedError( "Fixit commands are not allowed when the run is not in a recoverable state." ) - elif not self._state.failed_command or ( - self._state.failed_command - and self._state.failed_command.command.id - != action.request.failed_command_id - ): - raise FixitCommandNotAllowedError( - "Fixit command does not match failed command." - ) else: return action else: @@ -842,14 +834,14 @@ def validate_action_allowed( # noqa: C901 elif isinstance(action, ResumeFromRecoveryAction): if self.get_status() != EngineStatus.AWAITING_RECOVERY: - raise ResumeForRecoveryNotAllowedError( + raise ResumeFromRecoveryNotAllowedError( "Cannot resume from recovery if the run is not in recovery mode." ) elif ( self.get_status() == EngineStatus.AWAITING_RECOVERY and len(self._state.command_history.get_fixit_queue_ids()) > 0 ): - raise ResumeForRecoveryNotAllowedError( + raise ResumeFromRecoveryNotAllowedError( "Cannot resume from recovery while there are fixit commands in the queue." ) else: diff --git a/api/tests/opentrons/protocol_engine/state/test_command_view_old.py b/api/tests/opentrons/protocol_engine/state/test_command_view_old.py index eb649050ece..762064d47b1 100644 --- a/api/tests/opentrons/protocol_engine/state/test_command_view_old.py +++ b/api/tests/opentrons/protocol_engine/state/test_command_view_old.py @@ -552,7 +552,6 @@ class ActionAllowedSpec(NamedTuple): request=cmd.HomeCreate( params=cmd.HomeParams(), intent=cmd.CommandIntent.FIXIT, - failed_command_id="command-id-2", ), request_hash=None, command_id="command-id", @@ -581,7 +580,6 @@ class ActionAllowedSpec(NamedTuple): request=cmd.HomeCreate( params=cmd.HomeParams(), intent=cmd.CommandIntent.FIXIT, - failed_command_id="command-id-3", ), request_hash=None, command_id="command-id", @@ -609,9 +607,10 @@ class ActionAllowedSpec(NamedTuple): ), action=ResumeFromRecoveryAction(), expected_error=None, - ) + ), ] + @pytest.mark.parametrize(ActionAllowedSpec._fields, action_allowed_specs) def test_validate_action_allowed( subject: CommandView, diff --git a/robot-server/robot_server/runs/router/commands_router.py b/robot-server/robot_server/runs/router/commands_router.py index 0fe85df4867..fb5fd1a1e9d 100644 --- a/robot-server/robot_server/runs/router/commands_router.py +++ b/robot-server/robot_server/runs/router/commands_router.py @@ -139,10 +139,10 @@ async def get_current_run_engine_from_url( If you are running a protocol from a file(s), then you will likely not need to enqueue protocol commands using this endpoint. - Fixit commands may be enqueued anytime using this endpoint. - These commands are intended to fix a failed command. - They will be executed right after the failed command - and only if the run is in a paused state. + Fixit commands may be enqueued while the run is `awaiting-recovery` state. + These commands are intended to fix a failed command. + They will be executed right after the failed command + and only if the run is in a `awaiting-recovery` state. Once enqueued, setup commands will execute immediately with priority, while protocol commands will wait until a `play` action is issued. @@ -193,6 +193,12 @@ async def create_run_command( " the default was 30 seconds, not infinite." ), ), + failed_command_id: Optional[str] = Query( + default=None, + description=( + "FIXIT command use only. Reference of the failed command id we are trying to fix." + ), + ), protocol_engine: ProtocolEngine = Depends(get_current_run_engine_from_url), check_estop: bool = Depends(require_estop_in_good_state), ) -> PydanticResponse[SimpleBody[pe_commands.Command]]: @@ -205,6 +211,8 @@ async def create_run_command( Else, return immediately. Comes from a query parameter in the URL. timeout: The maximum time, in seconds, to wait before returning. Comes from a query parameter in the URL. + failed_command_id: FIXIT command use only. + Reference of the failed command id we are trying to fix. protocol_engine: The run's `ProtocolEngine` on which the new command will be enqueued. check_estop: Dependency to verify the estop is in a valid state. From 9dfa58ac44524eb19512a9319922f373bb274741 Mon Sep 17 00:00:00 2001 From: tamarzanzouri Date: Mon, 15 Apr 2024 16:24:55 -0400 Subject: [PATCH 13/41] removed tests that are not in prod --- .../state/test_command_store_old.py | 315 ------------------ 1 file changed, 315 deletions(-) diff --git a/api/tests/opentrons/protocol_engine/state/test_command_store_old.py b/api/tests/opentrons/protocol_engine/state/test_command_store_old.py index e527bc7784c..57bba2b4c90 100644 --- a/api/tests/opentrons/protocol_engine/state/test_command_store_old.py +++ b/api/tests/opentrons/protocol_engine/state/test_command_store_old.py @@ -430,321 +430,6 @@ def test_running_command_id() -> None: assert subject.state.command_history.get_running_command() is None -def test_command_failure_clears_queues() -> None: - """It should clear the command queue on command failure.""" - queue_1 = QueueCommandAction( - request=commands.WaitForResumeCreate( - params=commands.WaitForResumeParams(), key="command-key-1" - ), - request_hash=None, - created_at=datetime(year=2021, month=1, day=1), - command_id="command-id-1", - ) - queue_2 = QueueCommandAction( - request=commands.WaitForResumeCreate( - params=commands.WaitForResumeParams(), key="command-key-2" - ), - request_hash=None, - created_at=datetime(year=2021, month=1, day=1), - command_id="command-id-2", - ) - run_1 = RunCommandAction( - command_id="command-id-1", - started_at=datetime(year=2022, month=2, day=2), - ) - fail_1 = FailCommandAction( - command_id="command-id-1", - error_id="error-id", - failed_at=datetime(year=2023, month=3, day=3), - error=errors.ProtocolEngineError(message="oh no"), - notes=[ - CommandNote( - noteKind="noteKind", - shortMessage="shortMessage", - longMessage="longMessage", - source="source", - ) - ], - type=ErrorRecoveryType.FAIL_RUN, - ) - - expected_failed_1 = commands.WaitForResume( - id="command-id-1", - key="command-key-1", - error=errors.ErrorOccurrence( - id="error-id", - createdAt=datetime(year=2023, month=3, day=3), - errorCode=ErrorCodes.GENERAL_ERROR.value.code, - errorType="ProtocolEngineError", - detail="oh no", - ), - notes=[ - CommandNote( - noteKind="noteKind", - shortMessage="shortMessage", - longMessage="longMessage", - source="source", - ) - ], - createdAt=datetime(year=2021, month=1, day=1), - startedAt=datetime(year=2022, month=2, day=2), - completedAt=datetime(year=2023, month=3, day=3), - params=commands.WaitForResumeParams(), - status=commands.CommandStatus.FAILED, - ) - expected_failed_2 = commands.WaitForResume( - id="command-id-2", - key="command-key-2", - error=None, - createdAt=datetime(year=2021, month=1, day=1), - completedAt=datetime(year=2023, month=3, day=3), - params=commands.WaitForResumeParams(), - status=commands.CommandStatus.FAILED, - ) - - subject = CommandStore(is_door_open=False, config=_make_config()) - - subject.handle_action(queue_1) - subject.handle_action(queue_2) - subject.handle_action(run_1) - subject.handle_action(fail_1) - - assert subject.state.command_history.get_running_command() is None - assert subject.state.command_history.get_queue_ids() == OrderedSet() - assert subject.state.command_history.get_all_ids() == [ - "command-id-1", - "command-id-2", - ] - assert subject.state.command_history.get("command-id-1") == CommandEntry( - index=0, command=expected_failed_1 - ) - assert subject.state.command_history.get("command-id-2") == CommandEntry( - index=1, command=expected_failed_2 - ) - - -def test_setup_command_failure_only_clears_setup_command_queue() -> None: - """It should clear only the setup command queue for a failed setup command. - - This test queues up a non-setup command followed by two setup commands, - then attempts to run and fail the first setup command and - """ - cmd_1_non_setup = commands.WaitForResume( - id="command-id-1", - key="command-key-1", - createdAt=datetime(year=2021, month=1, day=1), - params=commands.WaitForResumeParams(), - status=commands.CommandStatus.QUEUED, - ) - queue_action_1_non_setup = QueueCommandAction( - request=commands.WaitForResumeCreate( - params=cmd_1_non_setup.params, key="command-key-1" - ), - request_hash=None, - created_at=datetime(year=2021, month=1, day=1), - command_id="command-id-1", - ) - queue_action_2_setup = QueueCommandAction( - request=commands.WaitForResumeCreate( - params=commands.WaitForResumeParams(), - intent=commands.CommandIntent.SETUP, - key="command-key-2", - ), - request_hash=None, - created_at=datetime(year=2021, month=1, day=1), - command_id="command-id-2", - ) - queue_action_3_setup = QueueCommandAction( - request=commands.WaitForResumeCreate( - params=commands.WaitForResumeParams(), - intent=commands.CommandIntent.SETUP, - key="command-key-3", - ), - request_hash=None, - created_at=datetime(year=2021, month=1, day=1), - command_id="command-id-3", - ) - - run_action_cmd_2 = RunCommandAction( - command_id="command-id-2", - started_at=datetime(year=2022, month=2, day=2), - ) - failed_action_cmd_2 = FailCommandAction( - command_id="command-id-2", - error_id="error-id", - failed_at=datetime(year=2023, month=3, day=3), - error=errors.ProtocolEngineError(message="oh no"), - notes=[ - CommandNote( - noteKind="noteKind", - shortMessage="shortMessage", - longMessage="longMessage", - source="source", - ) - ], - type=ErrorRecoveryType.FAIL_RUN, - ) - expected_failed_cmd_2 = commands.WaitForResume( - id="command-id-2", - key="command-key-2", - error=errors.ErrorOccurrence( - id="error-id", - createdAt=datetime(year=2023, month=3, day=3), - errorType="ProtocolEngineError", - detail="oh no", - errorCode=ErrorCodes.GENERAL_ERROR.value.code, - ), - notes=[ - CommandNote( - noteKind="noteKind", - shortMessage="shortMessage", - longMessage="longMessage", - source="source", - ) - ], - createdAt=datetime(year=2021, month=1, day=1), - startedAt=datetime(year=2022, month=2, day=2), - completedAt=datetime(year=2023, month=3, day=3), - params=commands.WaitForResumeParams(), - status=commands.CommandStatus.FAILED, - intent=commands.CommandIntent.SETUP, - ) - expected_failed_cmd_3 = commands.WaitForResume( - id="command-id-3", - key="command-key-3", - error=None, - createdAt=datetime(year=2021, month=1, day=1), - completedAt=datetime(year=2023, month=3, day=3), - params=commands.WaitForResumeParams(), - status=commands.CommandStatus.FAILED, - intent=commands.CommandIntent.SETUP, - ) - - subject = CommandStore(is_door_open=False, config=_make_config()) - - subject.handle_action(queue_action_1_non_setup) - subject.handle_action(queue_action_2_setup) - subject.handle_action(queue_action_3_setup) - subject.handle_action(run_action_cmd_2) - subject.handle_action(failed_action_cmd_2) - - assert subject.state.command_history.get_running_command() is None - assert subject.state.command_history.get_setup_queue_ids() == OrderedSet() - assert subject.state.command_history.get_queue_ids() == OrderedSet(["command-id-1"]) - assert subject.state.command_history.get_all_ids() == [ - "command-id-1", - "command-id-2", - "command-id-3", - ] - assert subject.state.command_history.get("command-id-1") == CommandEntry( - index=0, command=cmd_1_non_setup - ) - assert subject.state.command_history.get("command-id-2") == CommandEntry( - index=1, command=expected_failed_cmd_2 - ) - assert subject.state.command_history.get("command-id-3") == CommandEntry( - index=2, command=expected_failed_cmd_3 - ) - - -def test_nonfatal_command_failure() -> None: - """Test the command queue if a command fails recoverably. - - Commands that were after the failed command in the queue should be left in - the queue. - """ - queue_1 = QueueCommandAction( - request=commands.WaitForResumeCreate( - params=commands.WaitForResumeParams(), key="command-key-1" - ), - request_hash=None, - created_at=datetime(year=2021, month=1, day=1), - command_id="command-id-1", - ) - queue_2 = QueueCommandAction( - request=commands.WaitForResumeCreate( - params=commands.WaitForResumeParams(), key="command-key-2" - ), - request_hash=None, - created_at=datetime(year=2021, month=1, day=1), - command_id="command-id-2", - ) - run_1 = RunCommandAction( - command_id="command-id-1", - started_at=datetime(year=2022, month=2, day=2), - ) - fail_1 = FailCommandAction( - command_id="command-id-1", - error_id="error-id", - failed_at=datetime(year=2023, month=3, day=3), - error=errors.ProtocolEngineError(message="oh no"), - notes=[ - CommandNote( - noteKind="noteKind", - shortMessage="shortMessage", - longMessage="longMessage", - source="source", - ) - ], - type=ErrorRecoveryType.WAIT_FOR_RECOVERY, - ) - - expected_failed_1 = commands.WaitForResume( - id="command-id-1", - key="command-key-1", - error=errors.ErrorOccurrence( - id="error-id", - createdAt=datetime(year=2023, month=3, day=3), - errorCode=ErrorCodes.GENERAL_ERROR.value.code, - errorType="ProtocolEngineError", - detail="oh no", - ), - notes=[ - CommandNote( - noteKind="noteKind", - shortMessage="shortMessage", - longMessage="longMessage", - source="source", - ) - ], - createdAt=datetime(year=2021, month=1, day=1), - startedAt=datetime(year=2022, month=2, day=2), - completedAt=datetime(year=2023, month=3, day=3), - params=commands.WaitForResumeParams(), - status=commands.CommandStatus.FAILED, - ) - expected_queued_2 = commands.WaitForResume( - id="command-id-2", - key="command-key-2", - error=None, - createdAt=datetime(year=2021, month=1, day=1), - startedAt=None, - completedAt=None, - params=commands.WaitForResumeParams(), - status=commands.CommandStatus.QUEUED, - ) - - subject = CommandStore(is_door_open=False, config=_make_config()) - - subject.handle_action(queue_1) - subject.handle_action(queue_2) - subject.handle_action(run_1) - subject.handle_action(fail_1) - - assert subject.state.command_history.get_running_command() is None - assert subject.state.command_history.get_queue_ids() == OrderedSet(["command-id-2"]) - assert subject.state.command_history.get_all_ids() == [ - "command-id-1", - "command-id-2", - ] - assert subject.state.command_history.get("command-id-1") == CommandEntry( - index=0, command=expected_failed_1 - ) - assert subject.state.command_history.get("command-id-2") == CommandEntry( - index=1, command=expected_queued_2 - ) - - def test_command_store_keeps_commands_in_queue_order() -> None: """It should keep commands in the order they were originally enqueued.""" command_create_1_non_setup = commands.CommentCreate( From a20fdda41bd01af943863dbe8b02fdff2494cc3b Mon Sep 17 00:00:00 2001 From: tamarzanzouri Date: Mon, 15 Apr 2024 21:48:56 -0400 Subject: [PATCH 14/41] WIP failed_command_id from router to BaseCommand --- api/src/opentrons/protocol_engine/actions/actions.py | 1 + api/src/opentrons/protocol_engine/protocol_engine.py | 5 ++++- api/src/opentrons/protocol_engine/state/commands.py | 1 + .../protocol_engine/state/test_command_store_old.py | 3 --- robot-server/robot_server/runs/router/commands_router.py | 2 +- 5 files changed, 7 insertions(+), 5 deletions(-) diff --git a/api/src/opentrons/protocol_engine/actions/actions.py b/api/src/opentrons/protocol_engine/actions/actions.py index 2d46f614ec3..64e80fb1051 100644 --- a/api/src/opentrons/protocol_engine/actions/actions.py +++ b/api/src/opentrons/protocol_engine/actions/actions.py @@ -116,6 +116,7 @@ class QueueCommandAction: created_at: datetime request: CommandCreate request_hash: Optional[str] + failed_command_id: Optional[str] @dataclass(frozen=True) diff --git a/api/src/opentrons/protocol_engine/protocol_engine.py b/api/src/opentrons/protocol_engine/protocol_engine.py index 94af48ae97f..003caacb0c7 100644 --- a/api/src/opentrons/protocol_engine/protocol_engine.py +++ b/api/src/opentrons/protocol_engine/protocol_engine.py @@ -178,7 +178,9 @@ def resume_from_recovery(self) -> None: ) self._action_dispatcher.dispatch(action) - def add_command(self, request: commands.CommandCreate) -> commands.Command: + def add_command( + self, request: commands.CommandCreate, failed_command_id: Optional[str] = None + ) -> commands.Command: """Add a command to the `ProtocolEngine`'s queue. Arguments: @@ -211,6 +213,7 @@ def add_command(self, request: commands.CommandCreate) -> commands.Command: request_hash=request_hash, command_id=command_id, created_at=self._model_utils.get_timestamp(), + failed_command_id=failed_command_id, ) ) self._action_dispatcher.dispatch(action) diff --git a/api/src/opentrons/protocol_engine/state/commands.py b/api/src/opentrons/protocol_engine/state/commands.py index b7c8bae4611..e3acd86b412 100644 --- a/api/src/opentrons/protocol_engine/state/commands.py +++ b/api/src/opentrons/protocol_engine/state/commands.py @@ -243,6 +243,7 @@ def handle_action(self, action: Action) -> None: # noqa: C901 params=action.request.params, # type: ignore[arg-type] intent=action.request.intent, status=CommandStatus.QUEUED, + failed_command_id=action.failed_command_id, ) self._state.command_history.set_command_queued(queued_command) diff --git a/api/tests/opentrons/protocol_engine/state/test_command_store_old.py b/api/tests/opentrons/protocol_engine/state/test_command_store_old.py index 57bba2b4c90..a859ae7573b 100644 --- a/api/tests/opentrons/protocol_engine/state/test_command_store_old.py +++ b/api/tests/opentrons/protocol_engine/state/test_command_store_old.py @@ -14,12 +14,10 @@ from opentrons.ordered_set import OrderedSet from opentrons.protocol_engine.actions.actions import RunCommandAction -from opentrons.protocol_engine.notes.notes import CommandNote from opentrons.types import MountType, DeckSlotName from opentrons.hardware_control.types import DoorState from opentrons.protocol_engine import commands, errors -from opentrons.protocol_engine.error_recovery_policy import ErrorRecoveryType from opentrons.protocol_engine.types import DeckSlotLocation, DeckType, WellLocation from opentrons.protocol_engine.state import Config from opentrons.protocol_engine.state.commands import ( @@ -33,7 +31,6 @@ from opentrons.protocol_engine.actions import ( QueueCommandAction, SucceedCommandAction, - FailCommandAction, PlayAction, PauseAction, PauseSource, diff --git a/robot-server/robot_server/runs/router/commands_router.py b/robot-server/robot_server/runs/router/commands_router.py index fb5fd1a1e9d..716607bcc8b 100644 --- a/robot-server/robot_server/runs/router/commands_router.py +++ b/robot-server/robot_server/runs/router/commands_router.py @@ -223,7 +223,7 @@ async def create_run_command( command_create = request_body.data.copy(update={"intent": command_intent}) try: - command = protocol_engine.add_command(command_create) + command = protocol_engine.add_command(command_create, failed_command_id) except pe_errors.SetupCommandNotAllowedError as e: raise CommandNotAllowed.from_exc(e).as_error(status.HTTP_409_CONFLICT) From 5e631ec3bc5fa165e70fc7e6fbfc44de0c5aaba1 Mon Sep 17 00:00:00 2001 From: tamarzanzouri Date: Mon, 15 Apr 2024 22:12:52 -0400 Subject: [PATCH 15/41] fixed fixit in hash, renaming to protocol hash WIP --- .../protocol_engine/commands/hash_command_params.py | 2 +- api/src/opentrons/protocol_engine/protocol_engine.py | 9 ++++----- api/src/opentrons/protocol_engine/state/commands.py | 12 ++++++------ 3 files changed, 11 insertions(+), 12 deletions(-) diff --git a/api/src/opentrons/protocol_engine/commands/hash_command_params.py b/api/src/opentrons/protocol_engine/commands/hash_command_params.py index 39a042e55dd..52e67744eb8 100644 --- a/api/src/opentrons/protocol_engine/commands/hash_command_params.py +++ b/api/src/opentrons/protocol_engine/commands/hash_command_params.py @@ -28,7 +28,7 @@ def hash_command_params( The command hash, if the command is a protocol command. `None` if the command is a setup command. """ - if create.intent == CommandIntent.SETUP: + if create.intent in (CommandIntent.SETUP, CommandIntent.FIXIT): return None else: # We avoid Python's built-in hash() function because it's not stable across diff --git a/api/src/opentrons/protocol_engine/protocol_engine.py b/api/src/opentrons/protocol_engine/protocol_engine.py index 003caacb0c7..0cd9e2bc78a 100644 --- a/api/src/opentrons/protocol_engine/protocol_engine.py +++ b/api/src/opentrons/protocol_engine/protocol_engine.py @@ -201,11 +201,10 @@ def add_command( ) command_id = self._model_utils.generate_id() - if request.intent != commands.CommandIntent.FIXIT: - request_hash = commands.hash_command_params( - create=request, - last_hash=self._state_store.commands.get_latest_command_hash(), - ) + request_hash = commands.hash_command_params( + create=request, + last_hash=self._state_store.commands.get_latest_protocol_command_hash(), + ) action = self.state_view.commands.validate_action_allowed( QueueCommandAction( diff --git a/api/src/opentrons/protocol_engine/state/commands.py b/api/src/opentrons/protocol_engine/state/commands.py index e3acd86b412..77e1292b1fb 100644 --- a/api/src/opentrons/protocol_engine/state/commands.py +++ b/api/src/opentrons/protocol_engine/state/commands.py @@ -186,8 +186,8 @@ class CommandState: finish_error: Optional[ErrorOccurrence] """The error that happened during the post-run finish steps (homing & dropping tips), if any.""" - latest_command_hash: Optional[str] - """The latest hash value received in a QueueCommandAction. + latest_protocol_command_hash: Optional[str] + """The latest PROTOCOL command hash value received in a QueueCommandAction. This value can be used to generate future hashes. """ @@ -221,7 +221,7 @@ def __init__( recovery_target_command_id=None, run_completed_at=None, run_started_at=None, - latest_command_hash=None, + latest_protocol_command_hash=None, stopped_by_estop=False, ) @@ -249,7 +249,7 @@ def handle_action(self, action: Action) -> None: # noqa: C901 self._state.command_history.set_command_queued(queued_command) if action.request_hash is not None: - self._state.latest_command_hash = action.request_hash + self._state.latest_protocol_command_hash = action.request_hash elif isinstance(action, RunCommandAction): prev_entry = self._state.command_history.get(action.command_id) @@ -896,6 +896,6 @@ def get_status(self) -> EngineStatus: # SETUP and we're currently a setup command? return EngineStatus.IDLE - def get_latest_command_hash(self) -> Optional[str]: + def get_latest_protocol_command_hash(self) -> Optional[str]: """Get the command hash of the last queued command, if any.""" - return self._state.latest_command_hash + return self._state.latest_protocol_command_hash From f7511c023a9ad67d6434d3a21972f14ecd14ca8a Mon Sep 17 00:00:00 2001 From: tamarzanzouri Date: Wed, 17 Apr 2024 14:19:19 -0400 Subject: [PATCH 16/41] has protocol and command queue action set to None --- .../protocol_engine/actions/actions.py | 2 +- .../state/test_command_store_old.py | 26 +++++++++---------- .../state/test_command_view_old.py | 4 +-- .../protocol_engine/test_protocol_engine.py | 4 ++- 4 files changed, 19 insertions(+), 17 deletions(-) diff --git a/api/src/opentrons/protocol_engine/actions/actions.py b/api/src/opentrons/protocol_engine/actions/actions.py index 64e80fb1051..adcf4f9e40b 100644 --- a/api/src/opentrons/protocol_engine/actions/actions.py +++ b/api/src/opentrons/protocol_engine/actions/actions.py @@ -116,7 +116,7 @@ class QueueCommandAction: created_at: datetime request: CommandCreate request_hash: Optional[str] - failed_command_id: Optional[str] + failed_command_id: Optional[str] = None @dataclass(frozen=True) diff --git a/api/tests/opentrons/protocol_engine/state/test_command_store_old.py b/api/tests/opentrons/protocol_engine/state/test_command_store_old.py index a859ae7573b..f79e73e72d1 100644 --- a/api/tests/opentrons/protocol_engine/state/test_command_store_old.py +++ b/api/tests/opentrons/protocol_engine/state/test_command_store_old.py @@ -84,7 +84,7 @@ def test_initial_state( failed_command=None, command_error_recovery_types={}, recovery_target_command_id=None, - latest_command_hash=None, + latest_protocol_command_hash=None, stopped_by_estop=False, ) @@ -254,7 +254,7 @@ def test_command_queue_with_hash() -> None: ) assert subject.state.command_history.get("command-id-1").command.key == "abc123" - assert subject.state.latest_command_hash == "abc123" + assert subject.state.latest_protocol_command_hash == "abc123" subject.handle_action( QueueCommandAction( @@ -265,7 +265,7 @@ def test_command_queue_with_hash() -> None: ) ) - assert subject.state.latest_command_hash == "def456" + assert subject.state.latest_protocol_command_hash == "def456" def test_command_queue_and_unqueue() -> None: @@ -518,7 +518,7 @@ def test_command_store_handles_pause_action(pause_source: PauseSource) -> None: failed_command=None, command_error_recovery_types={}, recovery_target_command_id=None, - latest_command_hash=None, + latest_protocol_command_hash=None, stopped_by_estop=False, ) @@ -545,7 +545,7 @@ def test_command_store_handles_play_action(pause_source: PauseSource) -> None: command_error_recovery_types={}, recovery_target_command_id=None, run_started_at=datetime(year=2021, month=1, day=1), - latest_command_hash=None, + latest_protocol_command_hash=None, stopped_by_estop=False, ) assert subject.state.command_history.get_running_command() is None @@ -577,7 +577,7 @@ def test_command_store_handles_finish_action() -> None: command_error_recovery_types={}, recovery_target_command_id=None, run_started_at=datetime(year=2021, month=1, day=1), - latest_command_hash=None, + latest_protocol_command_hash=None, stopped_by_estop=False, ) assert subject.state.command_history.get_running_command() is None @@ -624,7 +624,7 @@ def test_command_store_handles_stop_action(from_estop: bool) -> None: command_error_recovery_types={}, recovery_target_command_id=None, run_started_at=datetime(year=2021, month=1, day=1), - latest_command_hash=None, + latest_protocol_command_hash=None, stopped_by_estop=from_estop, ) assert subject.state.command_history.get_running_command() is None @@ -655,7 +655,7 @@ def test_command_store_cannot_restart_after_should_stop() -> None: command_error_recovery_types={}, recovery_target_command_id=None, run_started_at=None, - latest_command_hash=None, + latest_protocol_command_hash=None, stopped_by_estop=False, ) assert subject.state.command_history.get_running_command() is None @@ -787,7 +787,7 @@ def test_command_store_wraps_unknown_errors() -> None: failed_command=None, command_error_recovery_types={}, recovery_target_command_id=None, - latest_command_hash=None, + latest_protocol_command_hash=None, stopped_by_estop=False, ) assert subject.state.command_history.get_running_command() is None @@ -850,7 +850,7 @@ def __init__(self, message: str) -> None: command_error_recovery_types={}, recovery_target_command_id=None, run_started_at=None, - latest_command_hash=None, + latest_protocol_command_hash=None, stopped_by_estop=False, ) assert subject.state.command_history.get_running_command() is None @@ -883,7 +883,7 @@ def test_command_store_ignores_stop_after_graceful_finish() -> None: command_error_recovery_types={}, recovery_target_command_id=None, run_started_at=datetime(year=2021, month=1, day=1), - latest_command_hash=None, + latest_protocol_command_hash=None, stopped_by_estop=False, ) assert subject.state.command_history.get_running_command() is None @@ -916,7 +916,7 @@ def test_command_store_ignores_finish_after_non_graceful_stop() -> None: command_error_recovery_types={}, recovery_target_command_id=None, run_started_at=datetime(year=2021, month=1, day=1), - latest_command_hash=None, + latest_protocol_command_hash=None, stopped_by_estop=False, ) assert subject.state.command_history.get_running_command() is None @@ -945,7 +945,7 @@ def test_handles_hardware_stopped() -> None: command_error_recovery_types={}, recovery_target_command_id=None, run_started_at=None, - latest_command_hash=None, + latest_protocol_command_hash=None, stopped_by_estop=False, ) assert subject.state.command_history.get_running_command() is None diff --git a/api/tests/opentrons/protocol_engine/state/test_command_view_old.py b/api/tests/opentrons/protocol_engine/state/test_command_view_old.py index 762064d47b1..065386f09b9 100644 --- a/api/tests/opentrons/protocol_engine/state/test_command_view_old.py +++ b/api/tests/opentrons/protocol_engine/state/test_command_view_old.py @@ -97,7 +97,7 @@ def get_command_view( command_error_recovery_types=command_error_recovery_types or {}, recovery_target_command_id=recovery_target_command_id, run_started_at=run_started_at, - latest_command_hash=latest_command_hash, + latest_protocol_command_hash=latest_command_hash, stopped_by_estop=False, ) @@ -1046,4 +1046,4 @@ def test_get_slice_default_cursor_queued() -> None: def test_get_latest_command_hash() -> None: """It should get the latest command hash from state, if set.""" subject = get_command_view(latest_command_hash="abc123") - assert subject.get_latest_command_hash() == "abc123" + assert subject.get_latest_protocol_command_hash() == "abc123" diff --git a/api/tests/opentrons/protocol_engine/test_protocol_engine.py b/api/tests/opentrons/protocol_engine/test_protocol_engine.py index 959c9172b9e..deb064fef99 100644 --- a/api/tests/opentrons/protocol_engine/test_protocol_engine.py +++ b/api/tests/opentrons/protocol_engine/test_protocol_engine.py @@ -204,7 +204,9 @@ def test_add_command( decoy.when(model_utils.generate_id()).then_return("command-id") decoy.when(model_utils.get_timestamp()).then_return(created_at) - decoy.when(state_store.commands.get_latest_command_hash()).then_return("abc") + decoy.when(state_store.commands.get_latest_protocol_command_hash()).then_return( + "abc" + ) decoy.when( commands.hash_command_params(create=standardized_request, last_hash="abc") ).then_return("123") From e8436d69228be30cf5a1c0c5c6523a3c3756c349 Mon Sep 17 00:00:00 2001 From: tamarzanzouri Date: Wed, 17 Apr 2024 15:48:25 -0400 Subject: [PATCH 17/41] moved setup and fixit hashing logic to add_command --- .../protocol_engine/commands/__init__.py | 4 +- .../commands/hash_command_params.py | 17 ++++----- .../protocol_engine/protocol_engine.py | 14 +++++-- .../commands/test_hash_command_params.py | 24 ++++++++---- .../state/test_command_view_old.py | 38 +------------------ .../protocol_engine/test_protocol_engine.py | 6 ++- 6 files changed, 41 insertions(+), 62 deletions(-) diff --git a/api/src/opentrons/protocol_engine/commands/__init__.py b/api/src/opentrons/protocol_engine/commands/__init__.py index 3dfe6eaf51f..7ce6e07eb68 100644 --- a/api/src/opentrons/protocol_engine/commands/__init__.py +++ b/api/src/opentrons/protocol_engine/commands/__init__.py @@ -19,7 +19,7 @@ from . import thermocycler from . import calibration -from .hash_command_params import hash_command_params +from .hash_command_params import hash_protocol_command_params from .generate_command_schema import generate_command_schema from .command import ( @@ -333,7 +333,7 @@ "CommandStatus", "CommandIntent", # command parameter hashing - "hash_command_params", + "hash_protocol_command_params", # command schema generation "generate_command_schema", # aspirate command models diff --git a/api/src/opentrons/protocol_engine/commands/hash_command_params.py b/api/src/opentrons/protocol_engine/commands/hash_command_params.py index 52e67744eb8..0cfd19d1af3 100644 --- a/api/src/opentrons/protocol_engine/commands/hash_command_params.py +++ b/api/src/opentrons/protocol_engine/commands/hash_command_params.py @@ -9,7 +9,7 @@ # TODO(mm, 2023-04-28): # This implementation will not notice that commands are different if they have different params # but share the same commandType. We should also hash command params. (Jira RCORE-326.) -def hash_command_params( +def hash_protocol_command_params( create: CommandCreate, last_hash: Optional[str] ) -> Optional[str]: """Given a command create object, return a hash. @@ -28,12 +28,9 @@ def hash_command_params( The command hash, if the command is a protocol command. `None` if the command is a setup command. """ - if create.intent in (CommandIntent.SETUP, CommandIntent.FIXIT): - return None - else: - # We avoid Python's built-in hash() function because it's not stable across - # runs of the Python interpreter. (Jira RSS-215.) - last_contribution = b"" if last_hash is None else last_hash.encode("ascii") - this_contribution = md5(create.commandType.encode("ascii")).digest() - to_hash = last_contribution + this_contribution - return md5(to_hash).hexdigest() + # We avoid Python's built-in hash() function because it's not stable across + # runs of the Python interpreter. (Jira RSS-215.) + last_contribution = b"" if last_hash is None else last_hash.encode("ascii") + this_contribution = md5(create.commandType.encode("ascii")).digest() + to_hash = last_contribution + this_contribution + return md5(to_hash).hexdigest() diff --git a/api/src/opentrons/protocol_engine/protocol_engine.py b/api/src/opentrons/protocol_engine/protocol_engine.py index 0cd9e2bc78a..bbf3ca2658c 100644 --- a/api/src/opentrons/protocol_engine/protocol_engine.py +++ b/api/src/opentrons/protocol_engine/protocol_engine.py @@ -201,10 +201,16 @@ def add_command( ) command_id = self._model_utils.generate_id() - request_hash = commands.hash_command_params( - create=request, - last_hash=self._state_store.commands.get_latest_protocol_command_hash(), - ) + if request.intent in ( + commands.CommandIntent.SETUP, + commands.CommandIntent.FIXIT, + ): + request_hash = None + else: + request_hash = commands.hash_protocol_command_params( + create=request, + last_hash=self._state_store.commands.get_latest_protocol_command_hash(), + ) action = self.state_view.commands.validate_action_allowed( QueueCommandAction( diff --git a/api/tests/opentrons/protocol_engine/commands/test_hash_command_params.py b/api/tests/opentrons/protocol_engine/commands/test_hash_command_params.py index 098ce53c321..fbb6293e7d2 100644 --- a/api/tests/opentrons/protocol_engine/commands/test_hash_command_params.py +++ b/api/tests/opentrons/protocol_engine/commands/test_hash_command_params.py @@ -2,7 +2,9 @@ from opentrons.protocol_engine import CommandIntent from opentrons.protocol_engine import commands -from opentrons.protocol_engine.commands.hash_command_params import hash_command_params +from opentrons.protocol_engine.commands.hash_command_params import ( + hash_protocol_command_params, +) def test_equivalent_commands() -> None: @@ -20,10 +22,14 @@ def test_equivalent_commands() -> None: params=commands.WaitForDurationParams(seconds=123) ) - assert hash_command_params(b, None) == hash_command_params(c, None) + assert hash_protocol_command_params(b, None) == hash_protocol_command_params( + c, None + ) - a_hash = hash_command_params(a, None) - assert hash_command_params(b, a_hash) == hash_command_params(c, a_hash) + a_hash = hash_protocol_command_params(a, None) + assert hash_protocol_command_params(b, a_hash) == hash_protocol_command_params( + c, a_hash + ) def test_nonequivalent_commands() -> None: @@ -38,7 +44,9 @@ def test_nonequivalent_commands() -> None: params=commands.WaitForDurationParams(seconds=123) ) - assert hash_command_params(a, None) != hash_command_params(b, None) + assert hash_protocol_command_params(a, None) != hash_protocol_command_params( + b, None + ) def test_repeated_commands() -> None: @@ -50,8 +58,8 @@ def test_repeated_commands() -> None: params=commands.WaitForDurationParams(seconds=123) ) - a_hash = hash_command_params(a, None) - b_hash = hash_command_params(b, a_hash) + a_hash = hash_protocol_command_params(a, None) + b_hash = hash_protocol_command_params(b, a_hash) assert a_hash != b_hash @@ -61,4 +69,4 @@ def test_setup_command() -> None: params=commands.WaitForDurationParams(seconds=123), intent=CommandIntent.SETUP, ) - assert hash_command_params(setup_command, None) is None + assert hash_protocol_command_params(setup_command, None) is None diff --git a/api/tests/opentrons/protocol_engine/state/test_command_view_old.py b/api/tests/opentrons/protocol_engine/state/test_command_view_old.py index 065386f09b9..a8436cad6ed 100644 --- a/api/tests/opentrons/protocol_engine/state/test_command_view_old.py +++ b/api/tests/opentrons/protocol_engine/state/test_command_view_old.py @@ -510,13 +510,6 @@ class ActionAllowedSpec(NamedTuple): ), expected_error=errors.SetupCommandNotAllowedError, ), - # Resuming from error recovery is not implemented yet. - # https://opentrons.atlassian.net/browse/EXEC-301 - ActionAllowedSpec( - subject=get_command_view(), - action=ResumeFromRecoveryAction(), - expected_error=NotImplementedError, - ), # fixit command is disallowed if not in recovery mode ActionAllowedSpec( subject=get_command_view(queue_status=QueueStatus.RUNNING), @@ -531,34 +524,6 @@ class ActionAllowedSpec(NamedTuple): ), expected_error=errors.FixitCommandNotAllowedError, ), - ActionAllowedSpec( - subject=get_command_view( - queue_status=QueueStatus.AWAITING_RECOVERY, - failed_command=CommandEntry( - index=2, - command=create_failed_command( - command_id="command-id-3", - error=ErrorOccurrence( - id="error-id", - errorType="ProtocolEngineError", - createdAt=datetime(year=2022, month=2, day=2), - detail="oh no", - errorCode=ErrorCodes.GENERAL_ERROR.value.code, - ), - ), - ), - ), - action=QueueCommandAction( - request=cmd.HomeCreate( - params=cmd.HomeParams(), - intent=cmd.CommandIntent.FIXIT, - ), - request_hash=None, - command_id="command-id", - created_at=datetime(year=2021, month=1, day=1), - ), - expected_error=errors.FixitCommandNotAllowedError, - ), ActionAllowedSpec( subject=get_command_view( queue_status=QueueStatus.AWAITING_RECOVERY, @@ -587,6 +552,7 @@ class ActionAllowedSpec(NamedTuple): ), expected_error=None, ), + # resume from recovery not allowed if fixit commands in queue ActionAllowedSpec( subject=get_command_view( queue_status=QueueStatus.AWAITING_RECOVERY, @@ -606,7 +572,7 @@ class ActionAllowedSpec(NamedTuple): ), ), action=ResumeFromRecoveryAction(), - expected_error=None, + expected_error=errors.ResumeFromRecoveryNotAllowedError, ), ] diff --git a/api/tests/opentrons/protocol_engine/test_protocol_engine.py b/api/tests/opentrons/protocol_engine/test_protocol_engine.py index deb064fef99..84a019170b7 100644 --- a/api/tests/opentrons/protocol_engine/test_protocol_engine.py +++ b/api/tests/opentrons/protocol_engine/test_protocol_engine.py @@ -130,7 +130,7 @@ def _mock_slot_standardization_module( def _mock_hash_command_params_module( decoy: Decoy, monkeypatch: pytest.MonkeyPatch ) -> None: - hash_command_params = commands.hash_command_params + hash_command_params = commands.hash_protocol_command_params monkeypatch.setattr( commands, "hash_command_params", decoy.mock(func=hash_command_params) ) @@ -208,7 +208,9 @@ def test_add_command( "abc" ) decoy.when( - commands.hash_command_params(create=standardized_request, last_hash="abc") + commands.hash_protocol_command_params( + create=standardized_request, last_hash="abc" + ) ).then_return("123") def _stub_queued(*_a: object, **_k: object) -> None: From a66a8b95772315c4dcdf3b4fdd5e935a9974a8ff Mon Sep 17 00:00:00 2001 From: tamarzanzouri Date: Wed, 17 Apr 2024 16:21:34 -0400 Subject: [PATCH 18/41] removed raise and fix failing test --- .../protocol_engine/commands/pick_up_tip.py | 2 -- .../protocol_engine/test_protocol_engine.py | 12 ++++++------ 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/api/src/opentrons/protocol_engine/commands/pick_up_tip.py b/api/src/opentrons/protocol_engine/commands/pick_up_tip.py index aa0f3e27877..c4ddbefb338 100644 --- a/api/src/opentrons/protocol_engine/commands/pick_up_tip.py +++ b/api/src/opentrons/protocol_engine/commands/pick_up_tip.py @@ -84,8 +84,6 @@ async def execute(self, params: PickUpTipParams) -> PickUpTipResult: well_name=well_name, ) - raise TipNotAttachedError() - return PickUpTipResult( tipVolume=tip_geometry.volume, tipLength=tip_geometry.length, diff --git a/api/tests/opentrons/protocol_engine/test_protocol_engine.py b/api/tests/opentrons/protocol_engine/test_protocol_engine.py index 84a019170b7..157f1c46e4a 100644 --- a/api/tests/opentrons/protocol_engine/test_protocol_engine.py +++ b/api/tests/opentrons/protocol_engine/test_protocol_engine.py @@ -184,7 +184,7 @@ def test_add_command( original_request = commands.WaitForResumeCreate( params=commands.WaitForResumeParams() ) - standardized_request = commands.HomeCreate(params=commands.HomeParams()) + standardized_request = commands.HomeCreate(params=commands.HomeParams(), intent=commands.CommandIntent.PROTOCOL) queued = commands.Home( id="command-id", key="command-key", @@ -207,11 +207,11 @@ def test_add_command( decoy.when(state_store.commands.get_latest_protocol_command_hash()).then_return( "abc" ) - decoy.when( - commands.hash_protocol_command_params( - create=standardized_request, last_hash="abc" - ) - ).then_return("123") + # decoy.when( + # commands.hash_protocol_command_params( + # create=standardized_request, last_hash="abc" + # ) + # ).then_return("123") def _stub_queued(*_a: object, **_k: object) -> None: decoy.when(state_store.commands.get("command-id")).then_return(queued) From 239fad586a7dd39dcf3ef37cb357f419cc14813c Mon Sep 17 00:00:00 2001 From: tamarzanzouri Date: Thu, 18 Apr 2024 10:43:21 -0400 Subject: [PATCH 19/41] fixed rehearsal --- .../protocol_engine/test_protocol_engine.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/api/tests/opentrons/protocol_engine/test_protocol_engine.py b/api/tests/opentrons/protocol_engine/test_protocol_engine.py index 157f1c46e4a..0b120ac2f12 100644 --- a/api/tests/opentrons/protocol_engine/test_protocol_engine.py +++ b/api/tests/opentrons/protocol_engine/test_protocol_engine.py @@ -132,7 +132,7 @@ def _mock_hash_command_params_module( ) -> None: hash_command_params = commands.hash_protocol_command_params monkeypatch.setattr( - commands, "hash_command_params", decoy.mock(func=hash_command_params) + commands, "hash_protocol_command_params", decoy.mock(func=hash_command_params) ) @@ -207,11 +207,11 @@ def test_add_command( decoy.when(state_store.commands.get_latest_protocol_command_hash()).then_return( "abc" ) - # decoy.when( - # commands.hash_protocol_command_params( - # create=standardized_request, last_hash="abc" - # ) - # ).then_return("123") + decoy.when( + commands.hash_protocol_command_params( + create=standardized_request, last_hash="abc" + ) + ).then_return("123") def _stub_queued(*_a: object, **_k: object) -> None: decoy.when(state_store.commands.get("command-id")).then_return(queued) From 0090dd6bf9a4dddcc51b2be65003daa76773dbf5 Mon Sep 17 00:00:00 2001 From: tamarzanzouri Date: Thu, 18 Apr 2024 15:04:25 -0400 Subject: [PATCH 20/41] added assert for failed command id and tests --- .../commands/hash_command_params.py | 2 + .../protocol_engine/protocol_engine.py | 5 + .../protocol_engine/test_protocol_engine.py | 104 +++++++++++++++++- 3 files changed, 110 insertions(+), 1 deletion(-) diff --git a/api/src/opentrons/protocol_engine/commands/hash_command_params.py b/api/src/opentrons/protocol_engine/commands/hash_command_params.py index 0cfd19d1af3..9b927aab014 100644 --- a/api/src/opentrons/protocol_engine/commands/hash_command_params.py +++ b/api/src/opentrons/protocol_engine/commands/hash_command_params.py @@ -28,6 +28,8 @@ def hash_protocol_command_params( The command hash, if the command is a protocol command. `None` if the command is a setup command. """ + if create.intent != CommandIntent.PROTOCOL: + return None # We avoid Python's built-in hash() function because it's not stable across # runs of the Python interpreter. (Jira RSS-215.) last_contribution = b"" if last_hash is None else last_hash.encode("ascii") diff --git a/api/src/opentrons/protocol_engine/protocol_engine.py b/api/src/opentrons/protocol_engine/protocol_engine.py index bbf3ca2658c..170e5a3ad9d 100644 --- a/api/src/opentrons/protocol_engine/protocol_engine.py +++ b/api/src/opentrons/protocol_engine/protocol_engine.py @@ -200,6 +200,11 @@ def add_command( request, self.state_view.config.robot_type ) + if failed_command_id: + assert ( + request.intent == commands.CommandIntent.FIXIT + ), "failed command id should be supplied with a FIXIT command." + command_id = self._model_utils.generate_id() if request.intent in ( commands.CommandIntent.SETUP, diff --git a/api/tests/opentrons/protocol_engine/test_protocol_engine.py b/api/tests/opentrons/protocol_engine/test_protocol_engine.py index 0b120ac2f12..5fa664ef9a8 100644 --- a/api/tests/opentrons/protocol_engine/test_protocol_engine.py +++ b/api/tests/opentrons/protocol_engine/test_protocol_engine.py @@ -184,7 +184,9 @@ def test_add_command( original_request = commands.WaitForResumeCreate( params=commands.WaitForResumeParams() ) - standardized_request = commands.HomeCreate(params=commands.HomeParams(), intent=commands.CommandIntent.PROTOCOL) + standardized_request = commands.HomeCreate( + params=commands.HomeParams(), intent=commands.CommandIntent.PROTOCOL + ) queued = commands.Home( id="command-id", key="command-key", @@ -250,6 +252,106 @@ def _stub_queued(*_a: object, **_k: object) -> None: assert result == queued +def test_add_fixit_command( + decoy: Decoy, + state_store: StateStore, + action_dispatcher: ActionDispatcher, + model_utils: ModelUtils, + subject: ProtocolEngine, +) -> None: + """It should add a fixit command to the state from a request.""" + created_at = datetime(year=2021, month=1, day=1) + original_request = commands.WaitForResumeCreate( + params=commands.WaitForResumeParams() + ) + standardized_request = commands.HomeCreate( + params=commands.HomeParams(), intent=commands.CommandIntent.FIXIT + ) + queued = commands.Home( + id="command-id", + key="command-key", + status=commands.CommandStatus.QUEUED, + createdAt=created_at, + params=commands.HomeParams(), + ) + + robot_type: RobotType = "OT-3 Standard" + decoy.when(state_store.config).then_return( + Config(robot_type=robot_type, deck_type=DeckType.OT3_STANDARD) + ) + + decoy.when( + slot_standardization.standardize_command(original_request, robot_type) + ).then_return(standardized_request) + + decoy.when(model_utils.generate_id()).then_return("command-id") + decoy.when(model_utils.get_timestamp()).then_return(created_at) + + def _stub_queued(*_a: object, **_k: object) -> None: + decoy.when(state_store.commands.get("command-id")).then_return(queued) + + decoy.when( + state_store.commands.validate_action_allowed( + QueueCommandAction( + command_id="command-id", + created_at=created_at, + request=standardized_request, + request_hash=None, + ) + ) + ).then_return( + QueueCommandAction( + command_id="command-id-validated", + created_at=created_at, + request=standardized_request, + request_hash=None, + ) + ) + + decoy.when( + action_dispatcher.dispatch( + QueueCommandAction( + command_id="command-id-validated", + created_at=created_at, + request=standardized_request, + request_hash=None, + ) + ), + ).then_do(_stub_queued) + + result = subject.add_command(original_request) + print(result) + assert result == queued + + +def test_add_fixit_command_raises( + decoy: Decoy, + state_store: StateStore, + action_dispatcher: ActionDispatcher, + model_utils: ModelUtils, + subject: ProtocolEngine, +) -> None: + """It should raise if a failed_command_id is supplied without a fixit command.""" + original_request = commands.WaitForResumeCreate( + params=commands.WaitForResumeParams() + ) + standardized_request = commands.HomeCreate( + params=commands.HomeParams(), intent=commands.CommandIntent.PROTOCOL + ) + + robot_type: RobotType = "OT-3 Standard" + decoy.when(state_store.config).then_return( + Config(robot_type=robot_type, deck_type=DeckType.OT3_STANDARD) + ) + + decoy.when( + slot_standardization.standardize_command(original_request, robot_type) + ).then_return(standardized_request) + + with pytest.raises(AssertionError): + subject.add_command(original_request, "id-123") + + async def test_add_and_execute_command( decoy: Decoy, state_store: StateStore, From ca257777fcb8abd83a3e6d53deb1a65e70938a1b Mon Sep 17 00:00:00 2001 From: tamarzanzouri Date: Thu, 18 Apr 2024 16:04:19 -0400 Subject: [PATCH 21/41] bad request when giving failed command and non fixit --- robot-server/robot_server/runs/router/commands_router.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/robot-server/robot_server/runs/router/commands_router.py b/robot-server/robot_server/runs/router/commands_router.py index 716607bcc8b..9c9e17e154d 100644 --- a/robot-server/robot_server/runs/router/commands_router.py +++ b/robot-server/robot_server/runs/router/commands_router.py @@ -222,6 +222,12 @@ async def create_run_command( command_intent = request_body.data.intent or pe_commands.CommandIntent.SETUP command_create = request_body.data.copy(update={"intent": command_intent}) + if failed_command_id and command_intent != pe_commands.CommandIntent.FIXIT: + raise CommandNotAllowed( + title="failed command with a non fixit command", + detail="Cannot supply failed command id with a non fixit command.", + ).as_error(status.HTTP_400_BAD_REQUEST) + try: command = protocol_engine.add_command(command_create, failed_command_id) From 746b3e07bcbb30f7cc3e8b19d97aaed40c0ee358 Mon Sep 17 00:00:00 2001 From: tamarzanzouri Date: Thu, 18 Apr 2024 22:29:43 -0400 Subject: [PATCH 22/41] dont access private methods from tests WIP dont pick setup when in waiting for recovery --- .../opentrons/protocol_engine/state/commands.py | 6 +++++- .../protocol_engine/state/test_command_history.py | 12 ++++++------ .../state/test_command_view_old.py | 15 ++++++++++++++- 3 files changed, 25 insertions(+), 8 deletions(-) diff --git a/api/src/opentrons/protocol_engine/state/commands.py b/api/src/opentrons/protocol_engine/state/commands.py index 77e1292b1fb..c5ac11ddd7c 100644 --- a/api/src/opentrons/protocol_engine/state/commands.py +++ b/api/src/opentrons/protocol_engine/state/commands.py @@ -615,7 +615,11 @@ def get_next_to_execute(self) -> Optional[str]: # if there is a setup command queued, prioritize it next_setup_cmd = self._state.command_history.get_setup_queue_ids().head(None) - if self._state.queue_status != QueueStatus.PAUSED and next_setup_cmd: + if ( + self._state.queue_status + not in [QueueStatus.PAUSED, QueueStatus.AWAITING_RECOVERY] + and next_setup_cmd + ): return next_setup_cmd # if the queue is running, return the next protocol command diff --git a/api/tests/opentrons/protocol_engine/state/test_command_history.py b/api/tests/opentrons/protocol_engine/state/test_command_history.py index cacf5fa39cb..269baa8f172 100644 --- a/api/tests/opentrons/protocol_engine/state/test_command_history.py +++ b/api/tests/opentrons/protocol_engine/state/test_command_history.py @@ -204,12 +204,12 @@ def test_set_running_command_id(command_history: CommandHistory) -> None: def test_set_fixit_running_command_id(command_history: CommandHistory) -> None: """It should set the ID of the currently running fixit command.""" - command_entry = create_queued_command_entry() - command_history._add("0", command_entry) - command_history._set_running_command_id("0") - fixit_command_entry = create_fixit_command_entry() - command_history._add("1", fixit_command_entry) - command_history._set_running_command_id("1") + command_entry = create_queued_command() + command_history.set_command_queued(command_entry) + command_history.set_command_running(command_entry) + fixit_command_entry = create_queued_command(intent=CommandIntent.FIXIT) + command_history.set_command_queued(fixit_command_entry) + command_history.set_command_running(fixit_command_entry) assert command_history.get_running_command() == fixit_command_entry assert command_history.get_all_commands() == [ command_entry.command, diff --git a/api/tests/opentrons/protocol_engine/state/test_command_view_old.py b/api/tests/opentrons/protocol_engine/state/test_command_view_old.py index a8436cad6ed..2ab6030a919 100644 --- a/api/tests/opentrons/protocol_engine/state/test_command_view_old.py +++ b/api/tests/opentrons/protocol_engine/state/test_command_view_old.py @@ -157,7 +157,7 @@ def test_get_next_to_execute_prioritizes_setup_command_queue( queued_setup_command_ids=["setup-command-id"], ) - assert subject.get_next_to_execute() == "setup-command-id" + assert subject.get_next_to_execute() is None @pytest.mark.parametrize( @@ -216,6 +216,19 @@ def test_get_next_to_execute_returns_no_commands_if_paused() -> None: assert result is None +def test_get_next_to_execute_returns_no_commands_if_awaiting_recovery_no_fixit() -> None: + """It should not return any type of command if the engine is awaiting-recovery.""" + subject = get_command_view( + queue_status=QueueStatus.AWAITING_RECOVERY, + queued_setup_command_ids=["setup-id-1", "setup-id-2"], + queued_command_ids=["command-id-1", "command-id-2"], + queued_fixit_command_ids=[], + ) + result = subject.get_next_to_execute() + + assert result is None + + @pytest.mark.parametrize("run_result", RunResult) def test_get_next_to_execute_raises_if_stopped(run_result: RunResult) -> None: """It should raise if an engine stop has been requested.""" From 86e96513dad586f0e382a351fd298f3250daef8f Mon Sep 17 00:00:00 2001 From: tamarzanzouri Date: Fri, 19 Apr 2024 11:20:44 -0400 Subject: [PATCH 23/41] do not access private methods from test --- .../state/test_command_history.py | 32 +++++++++++++++---- 1 file changed, 25 insertions(+), 7 deletions(-) diff --git a/api/tests/opentrons/protocol_engine/state/test_command_history.py b/api/tests/opentrons/protocol_engine/state/test_command_history.py index 269baa8f172..43300eddafc 100644 --- a/api/tests/opentrons/protocol_engine/state/test_command_history.py +++ b/api/tests/opentrons/protocol_engine/state/test_command_history.py @@ -5,7 +5,7 @@ from opentrons.protocol_engine.errors.exceptions import CommandDoesNotExistError from opentrons.protocol_engine.state.command_history import CommandHistory, CommandEntry -from opentrons.protocol_engine.commands import CommandIntent +from opentrons.protocol_engine.commands import CommandIntent, CommandStatus from .command_fixtures import ( create_queued_command, @@ -206,14 +206,32 @@ def test_set_fixit_running_command_id(command_history: CommandHistory) -> None: """It should set the ID of the currently running fixit command.""" command_entry = create_queued_command() command_history.set_command_queued(command_entry) - command_history.set_command_running(command_entry) - fixit_command_entry = create_queued_command(intent=CommandIntent.FIXIT) + running_command = command_entry.copy( + update={ + "status": CommandStatus.RUNNING, + } + ) + command_history.set_command_running(running_command) + finished_command = command_entry.copy( + update={ + "status": CommandStatus.SUCCEEDED, + } + ) + command_history.set_command_succeeded(finished_command) + fixit_command_entry = create_queued_command( + command_id="fixit-id", intent=CommandIntent.FIXIT + ) command_history.set_command_queued(fixit_command_entry) - command_history.set_command_running(fixit_command_entry) - assert command_history.get_running_command() == fixit_command_entry + fixit_running_command = fixit_command_entry.copy( + update={ + "status": CommandStatus.RUNNING, + } + ) + command_history.set_command_running(fixit_running_command) + assert command_history.get_running_command().command == fixit_running_command assert command_history.get_all_commands() == [ - command_entry.command, - fixit_command_entry.command, + finished_command, + fixit_running_command, ] From 25275555cbacf4551679de2fc1ee51385de99871 Mon Sep 17 00:00:00 2001 From: tamarzanzouri Date: Fri, 19 Apr 2024 13:10:26 -0400 Subject: [PATCH 24/41] changed all tests to not access private methods --- .../protocol_engine/actions/__init__.py | 2 -- .../protocol_engine/state/commands.py | 14 ++++++++++++++ .../state/test_command_history.py | 18 +++++++++++++----- .../state/test_command_view_old.py | 2 +- 4 files changed, 28 insertions(+), 8 deletions(-) diff --git a/api/src/opentrons/protocol_engine/actions/__init__.py b/api/src/opentrons/protocol_engine/actions/__init__.py index 4fd55f308b9..ac3fc653976 100644 --- a/api/src/opentrons/protocol_engine/actions/__init__.py +++ b/api/src/opentrons/protocol_engine/actions/__init__.py @@ -27,7 +27,6 @@ DoorChangeAction, ResetTipsAction, SetPipetteMovementSpeedAction, - ResumeFromRecoveryAction, ) __all__ = [ @@ -55,7 +54,6 @@ "DoorChangeAction", "ResetTipsAction", "SetPipetteMovementSpeedAction", - "ResumeFromRecoveryAction", # action payload values "PauseSource", "FinishErrorDetails", diff --git a/api/src/opentrons/protocol_engine/state/commands.py b/api/src/opentrons/protocol_engine/state/commands.py index c5ac11ddd7c..9bb6fd05379 100644 --- a/api/src/opentrons/protocol_engine/state/commands.py +++ b/api/src/opentrons/protocol_engine/state/commands.py @@ -324,6 +324,20 @@ def handle_action(self, action: Action) -> None: # noqa: C901 self._state.command_history.clear_queue() else: assert_never(action.type) + elif prev_entry.command.intent == CommandIntent.FIXIT: + other_command_ids_to_fail = ( + self._state.command_history.get_fixit_queue_ids() + ) + for command_id in other_command_ids_to_fail: + # TODO(mc, 2022-06-06): add new "cancelled" status or similar + self._update_to_failed( + command_id=command_id, + failed_at=action.failed_at, + error_occurrence=None, + error_recovery_type=None, + notes=None, + ) + self._state.command_history.clear_fixit_queue() else: assert_never(prev_entry.command.intent) diff --git a/api/tests/opentrons/protocol_engine/state/test_command_history.py b/api/tests/opentrons/protocol_engine/state/test_command_history.py index 43300eddafc..3c84b86e07f 100644 --- a/api/tests/opentrons/protocol_engine/state/test_command_history.py +++ b/api/tests/opentrons/protocol_engine/state/test_command_history.py @@ -228,7 +228,9 @@ def test_set_fixit_running_command_id(command_history: CommandHistory) -> None: } ) command_history.set_command_running(fixit_running_command) - assert command_history.get_running_command().command == fixit_running_command + current_running_command = command_history.get_running_command() + assert current_running_command is not None + assert current_running_command.command == fixit_running_command assert command_history.get_all_commands() == [ finished_command, fixit_running_command, @@ -249,8 +251,9 @@ def test_add_to_setup_queue(command_history: CommandHistory) -> None: def test_add_to_fixit_queue(command_history: CommandHistory) -> None: """It should add the given ID to the setup queue.""" - command_history._add_to_fixit_queue("0") - assert command_history.get_fixit_queue_ids() == OrderedSet(["0"]) + fixit_command = create_queued_command(intent=CommandIntent.FIXIT) + command_history.set_command_queued(fixit_command) + assert command_history.get_fixit_queue_ids() == OrderedSet(["command-id"]) def test_clear_queue(command_history: CommandHistory) -> None: @@ -271,8 +274,13 @@ def test_clear_setup_queue(command_history: CommandHistory) -> None: def test_clear_fixit_queue(command_history: CommandHistory) -> None: """It should clear all commands in the setup queue.""" - command_history._add_to_fixit_queue("0") - command_history._add_to_fixit_queue("1") + command_history.set_command_queued( + create_queued_command(command_id="0", intent=CommandIntent.FIXIT) + ) + command_history.set_command_queued( + create_queued_command(command_id="1", intent=CommandIntent.FIXIT) + ) + assert command_history.get_fixit_queue_ids() == OrderedSet(["0", "1"]) command_history.clear_fixit_queue() assert command_history.get_fixit_queue_ids() == OrderedSet() diff --git a/api/tests/opentrons/protocol_engine/state/test_command_view_old.py b/api/tests/opentrons/protocol_engine/state/test_command_view_old.py index 2ab6030a919..ddfcd66adb2 100644 --- a/api/tests/opentrons/protocol_engine/state/test_command_view_old.py +++ b/api/tests/opentrons/protocol_engine/state/test_command_view_old.py @@ -46,7 +46,7 @@ ) -def get_command_view( +def get_command_view( # noqa: C901 queue_status: QueueStatus = QueueStatus.SETUP, run_completed_at: Optional[datetime] = None, run_started_at: Optional[datetime] = None, From c736f813e9bb2fbcb846b81dcc8d1ac0166d99e8 Mon Sep 17 00:00:00 2001 From: tamarzanzouri Date: Fri, 19 Apr 2024 14:09:44 -0400 Subject: [PATCH 25/41] raise from add_command and catch in router --- .../protocol_engine/errors/__init__.py | 3 ++- .../protocol_engine/errors/exceptions.py | 13 ++++++++++ .../protocol_engine/protocol_engine.py | 12 +++++---- .../protocol_engine/test_protocol_engine.py | 7 ++++-- .../runs/router/commands_router.py | 8 ++---- .../tests/runs/router/test_commands_router.py | 25 +++++++++++++++++++ 6 files changed, 54 insertions(+), 14 deletions(-) diff --git a/api/src/opentrons/protocol_engine/errors/__init__.py b/api/src/opentrons/protocol_engine/errors/__init__.py index a255b08b49d..994e4cc9ed3 100644 --- a/api/src/opentrons/protocol_engine/errors/__init__.py +++ b/api/src/opentrons/protocol_engine/errors/__init__.py @@ -67,6 +67,7 @@ LocationIsStagingSlotError, InvalidAxisForRobotType, NotSupportedOnRobotType, + CommandNotAllowedError, ) from .error_occurrence import ErrorOccurrence, ProtocolCommandFailedError @@ -142,5 +143,5 @@ "NotSupportedOnRobotType", # error occurrence models "ErrorOccurrence", - "FailedGripperPickupError", + "CommandNotAllowedError", ] diff --git a/api/src/opentrons/protocol_engine/errors/exceptions.py b/api/src/opentrons/protocol_engine/errors/exceptions.py index 99c3166b8c7..9069f26b680 100644 --- a/api/src/opentrons/protocol_engine/errors/exceptions.py +++ b/api/src/opentrons/protocol_engine/errors/exceptions.py @@ -505,6 +505,19 @@ def __init__( super().__init__(ErrorCodes.POSITION_UNKNOWN, message, details, wrapping) +class CommandNotAllowedError(ProtocolEngineError): + """Raised when adding a command with bad data.""" + + def __init__( + self, + message: Optional[str] = None, + details: Optional[Dict[str, Any]] = None, + wrapping: Optional[Sequence[EnumeratedError]] = None, + ) -> None: + """Build a CommandNotAllowedError.""" + super().__init__(ErrorCodes.GENERAL_ERROR, message, details, wrapping) + + class FixitCommandNotAllowedError(ProtocolEngineError): """Raised when adding a fixit command to a non-recoverable engine.""" diff --git a/api/src/opentrons/protocol_engine/protocol_engine.py b/api/src/opentrons/protocol_engine/protocol_engine.py index 170e5a3ad9d..6cc82323c26 100644 --- a/api/src/opentrons/protocol_engine/protocol_engine.py +++ b/api/src/opentrons/protocol_engine/protocol_engine.py @@ -18,7 +18,7 @@ EnumeratedError, ) -from .errors import ProtocolCommandFailedError, ErrorOccurrence +from .errors import ProtocolCommandFailedError, ErrorOccurrence, CommandNotAllowedError from .errors.exceptions import EStopActivatedError from . import commands, slot_standardization from .resources import ModelUtils, ModuleDataProvider @@ -195,15 +195,17 @@ def add_command( but the engine was not idle or paused. RunStoppedError: the run has been stopped, so no new commands may be added. + CommandNotAllowedError: the request specified a failed command id + with a non fixit command. """ request = slot_standardization.standardize_command( request, self.state_view.config.robot_type ) - if failed_command_id: - assert ( - request.intent == commands.CommandIntent.FIXIT - ), "failed command id should be supplied with a FIXIT command." + if failed_command_id and request.intent != commands.CommandIntent.FIXIT: + raise CommandNotAllowedError( + "failed command id should be supplied with a FIXIT command." + ) command_id = self._model_utils.generate_id() if request.intent in ( diff --git a/api/tests/opentrons/protocol_engine/test_protocol_engine.py b/api/tests/opentrons/protocol_engine/test_protocol_engine.py index 5fa664ef9a8..74c58c962d4 100644 --- a/api/tests/opentrons/protocol_engine/test_protocol_engine.py +++ b/api/tests/opentrons/protocol_engine/test_protocol_engine.py @@ -19,7 +19,10 @@ from opentrons.protocols.models import LabwareDefinition from opentrons.protocol_engine import ProtocolEngine, commands, slot_standardization -from opentrons.protocol_engine.errors.exceptions import EStopActivatedError +from opentrons.protocol_engine.errors.exceptions import ( + EStopActivatedError, + CommandNotAllowedError, +) from opentrons.protocol_engine.types import ( DeckType, LabwareOffset, @@ -348,7 +351,7 @@ def test_add_fixit_command_raises( slot_standardization.standardize_command(original_request, robot_type) ).then_return(standardized_request) - with pytest.raises(AssertionError): + with pytest.raises(CommandNotAllowedError): subject.add_command(original_request, "id-123") diff --git a/robot-server/robot_server/runs/router/commands_router.py b/robot-server/robot_server/runs/router/commands_router.py index 9c9e17e154d..0661088e25b 100644 --- a/robot-server/robot_server/runs/router/commands_router.py +++ b/robot-server/robot_server/runs/router/commands_router.py @@ -222,12 +222,6 @@ async def create_run_command( command_intent = request_body.data.intent or pe_commands.CommandIntent.SETUP command_create = request_body.data.copy(update={"intent": command_intent}) - if failed_command_id and command_intent != pe_commands.CommandIntent.FIXIT: - raise CommandNotAllowed( - title="failed command with a non fixit command", - detail="Cannot supply failed command id with a non fixit command.", - ).as_error(status.HTTP_400_BAD_REQUEST) - try: command = protocol_engine.add_command(command_create, failed_command_id) @@ -235,6 +229,8 @@ async def create_run_command( raise CommandNotAllowed.from_exc(e).as_error(status.HTTP_409_CONFLICT) except pe_errors.RunStoppedError as e: raise RunStopped.from_exc(e).as_error(status.HTTP_409_CONFLICT) + except pe_errors.CommandNotAllowedError as e: + raise CommandNotAllowed.from_exc(e).as_error(status.HTTP_400_BAD_REQUEST) if waitUntilComplete: timeout_sec = None if timeout is None else timeout / 1000.0 diff --git a/robot-server/tests/runs/router/test_commands_router.py b/robot-server/tests/runs/router/test_commands_router.py index fa5e47ada9a..89682fa772f 100644 --- a/robot-server/tests/runs/router/test_commands_router.py +++ b/robot-server/tests/runs/router/test_commands_router.py @@ -132,6 +132,31 @@ def _stub_queued_command_state(*_a: object, **_k: object) -> pe_commands.Command decoy.verify(await mock_protocol_engine.wait_for_command("command-id"), times=0) +async def test_create_command_with_failed_command_raises( + decoy: Decoy, + protocol_engine: ProtocolEngine, +) -> None: + """It should return 400 bad request.""" + command_create = pe_commands.HomeCreate(params=pe_commands.HomeParams()) + + decoy.when( + protocol_engine.add_command( + pe_commands.HomeCreate( + params=pe_commands.HomeParams(), + intent=pe_commands.CommandIntent.SETUP, + ) + ) + ).then_raise(pe_errors.CommandNotAllowedError) + + with pytest.raises(ApiError): + await create_run_command( + RequestModelWithCommandCreate(data=command_create), + waitUntilComplete=False, + timeout=42, + engine=protocol_engine, + failed_command_id="123" + ) + async def test_create_run_command_blocking_completion( decoy: Decoy, mock_protocol_engine: ProtocolEngine, From 764bf9ddb2cb33d00912cf2f31bbb1dad348e98d Mon Sep 17 00:00:00 2001 From: tamarzanzouri Date: Fri, 19 Apr 2024 15:04:27 -0400 Subject: [PATCH 26/41] raise 400 with bad data --- .../runs/router/commands_router.py | 6 ++-- .../tests/runs/router/test_commands_router.py | 29 ++++++++++++------- 2 files changed, 22 insertions(+), 13 deletions(-) diff --git a/robot-server/robot_server/runs/router/commands_router.py b/robot-server/robot_server/runs/router/commands_router.py index 0661088e25b..51b66c9c744 100644 --- a/robot-server/robot_server/runs/router/commands_router.py +++ b/robot-server/robot_server/runs/router/commands_router.py @@ -221,9 +221,11 @@ async def create_run_command( # behavior is to pass through `command_intent` without overriding it command_intent = request_body.data.intent or pe_commands.CommandIntent.SETUP command_create = request_body.data.copy(update={"intent": command_intent}) - + print(failed_command_id) try: - command = protocol_engine.add_command(command_create, failed_command_id) + command = protocol_engine.add_command( + request=command_create, failed_command_id=failed_command_id + ) except pe_errors.SetupCommandNotAllowedError as e: raise CommandNotAllowed.from_exc(e).as_error(status.HTTP_409_CONFLICT) diff --git a/robot-server/tests/runs/router/test_commands_router.py b/robot-server/tests/runs/router/test_commands_router.py index 89682fa772f..24201aec8cf 100644 --- a/robot-server/tests/runs/router/test_commands_router.py +++ b/robot-server/tests/runs/router/test_commands_router.py @@ -114,10 +114,11 @@ def _stub_queued_command_state(*_a: object, **_k: object) -> pe_commands.Command decoy.when( mock_protocol_engine.add_command( - pe_commands.WaitForResumeCreate( + request=pe_commands.WaitForResumeCreate( params=pe_commands.WaitForResumeParams(message="Hello"), intent=pe_commands.CommandIntent.SETUP, - ) + ), + failed_command_id=None, ) ).then_do(_stub_queued_command_state) @@ -125,6 +126,7 @@ def _stub_queued_command_state(*_a: object, **_k: object) -> pe_commands.Command request_body=RequestModelWithCommandCreate(data=command_request), waitUntilComplete=False, protocol_engine=mock_protocol_engine, + failed_command_id=None, ) assert result.content.data == command_once_added @@ -134,29 +136,31 @@ def _stub_queued_command_state(*_a: object, **_k: object) -> pe_commands.Command async def test_create_command_with_failed_command_raises( decoy: Decoy, - protocol_engine: ProtocolEngine, + mock_protocol_engine: ProtocolEngine, ) -> None: """It should return 400 bad request.""" command_create = pe_commands.HomeCreate(params=pe_commands.HomeParams()) decoy.when( - protocol_engine.add_command( + mock_protocol_engine.add_command( pe_commands.HomeCreate( params=pe_commands.HomeParams(), intent=pe_commands.CommandIntent.SETUP, - ) + ), + failed_command_id="123", ) - ).then_raise(pe_errors.CommandNotAllowedError) + ).then_raise(pe_errors.CommandNotAllowedError()) with pytest.raises(ApiError): await create_run_command( RequestModelWithCommandCreate(data=command_create), waitUntilComplete=False, timeout=42, - engine=protocol_engine, - failed_command_id="123" + protocol_engine=mock_protocol_engine, + failed_command_id="123", ) + async def test_create_run_command_blocking_completion( decoy: Decoy, mock_protocol_engine: ProtocolEngine, @@ -196,7 +200,7 @@ def _stub_completed_command_state(*_a: object, **_k: object) -> None: mock_protocol_engine.state_view.commands.get("command-id") ).then_return(command_once_completed) - decoy.when(mock_protocol_engine.add_command(command_request)).then_do( + decoy.when(mock_protocol_engine.add_command(command_request, None)).then_do( _stub_queued_command_state ) @@ -209,6 +213,7 @@ def _stub_completed_command_state(*_a: object, **_k: object) -> None: waitUntilComplete=True, timeout=999, protocol_engine=mock_protocol_engine, + failed_command_id=None, ) assert result.content.data == command_once_completed @@ -225,7 +230,7 @@ async def test_add_conflicting_setup_command( intent=pe_commands.CommandIntent.SETUP, ) - decoy.when(mock_protocol_engine.add_command(command_request)).then_raise( + decoy.when(mock_protocol_engine.add_command(command_request, None)).then_raise( pe_errors.SetupCommandNotAllowedError("oh no") ) @@ -234,6 +239,7 @@ async def test_add_conflicting_setup_command( request_body=RequestModelWithCommandCreate(data=command_request), waitUntilComplete=False, protocol_engine=mock_protocol_engine, + failed_command_id=None, ) assert exc_info.value.status_code == 409 @@ -253,7 +259,7 @@ async def test_add_command_to_stopped_engine( intent=pe_commands.CommandIntent.SETUP, ) - decoy.when(mock_protocol_engine.add_command(command_request)).then_raise( + decoy.when(mock_protocol_engine.add_command(command_request, None)).then_raise( pe_errors.RunStoppedError("oh no") ) @@ -262,6 +268,7 @@ async def test_add_command_to_stopped_engine( request_body=RequestModelWithCommandCreate(data=command_request), waitUntilComplete=False, protocol_engine=mock_protocol_engine, + failed_command_id=None, ) assert exc_info.value.status_code == 409 From 9d464a6e21436e7322c1ca8b922872f0db3a9fbb Mon Sep 17 00:00:00 2001 From: TamarZanzouri Date: Fri, 19 Apr 2024 16:49:56 -0400 Subject: [PATCH 27/41] Update robot-server/robot_server/runs/router/commands_router.py --- robot-server/robot_server/runs/router/commands_router.py | 1 - 1 file changed, 1 deletion(-) diff --git a/robot-server/robot_server/runs/router/commands_router.py b/robot-server/robot_server/runs/router/commands_router.py index 51b66c9c744..577c479fb16 100644 --- a/robot-server/robot_server/runs/router/commands_router.py +++ b/robot-server/robot_server/runs/router/commands_router.py @@ -221,7 +221,6 @@ async def create_run_command( # behavior is to pass through `command_intent` without overriding it command_intent = request_body.data.intent or pe_commands.CommandIntent.SETUP command_create = request_body.data.copy(update={"intent": command_intent}) - print(failed_command_id) try: command = protocol_engine.add_command( request=command_create, failed_command_id=failed_command_id From b0249a77f75a29fc73b3a530deef2ccf79a51c7e Mon Sep 17 00:00:00 2001 From: TamarZanzouri Date: Fri, 19 Apr 2024 16:50:09 -0400 Subject: [PATCH 28/41] Update api/tests/opentrons/protocol_engine/test_protocol_engine.py --- api/tests/opentrons/protocol_engine/test_protocol_engine.py | 1 - 1 file changed, 1 deletion(-) diff --git a/api/tests/opentrons/protocol_engine/test_protocol_engine.py b/api/tests/opentrons/protocol_engine/test_protocol_engine.py index 74c58c962d4..6bd8e17003d 100644 --- a/api/tests/opentrons/protocol_engine/test_protocol_engine.py +++ b/api/tests/opentrons/protocol_engine/test_protocol_engine.py @@ -323,7 +323,6 @@ def _stub_queued(*_a: object, **_k: object) -> None: ).then_do(_stub_queued) result = subject.add_command(original_request) - print(result) assert result == queued From 42345640f15cca2535caaf49f2f919b84b14a67a Mon Sep 17 00:00:00 2001 From: tamarzanzouri Date: Fri, 19 Apr 2024 17:08:50 -0400 Subject: [PATCH 29/41] linting --- api/tests/opentrons/protocol_engine/test_protocol_engine.py | 1 - 1 file changed, 1 deletion(-) diff --git a/api/tests/opentrons/protocol_engine/test_protocol_engine.py b/api/tests/opentrons/protocol_engine/test_protocol_engine.py index 52519b4068e..2afcaf0f208 100644 --- a/api/tests/opentrons/protocol_engine/test_protocol_engine.py +++ b/api/tests/opentrons/protocol_engine/test_protocol_engine.py @@ -18,7 +18,6 @@ from opentrons.protocol_engine import ProtocolEngine, commands, slot_standardization from opentrons.protocol_engine.errors.exceptions import ( - EStopActivatedError, CommandNotAllowedError, ) from opentrons.protocol_engine.types import ( From 2120beeae9521678ea1f6e696d71e7d15750eb8d Mon Sep 17 00:00:00 2001 From: tamarzanzouri Date: Fri, 19 Apr 2024 17:08:59 -0400 Subject: [PATCH 30/41] linting --- api/src/opentrons/protocol_engine/commands/pick_up_tip.py | 1 - 1 file changed, 1 deletion(-) diff --git a/api/src/opentrons/protocol_engine/commands/pick_up_tip.py b/api/src/opentrons/protocol_engine/commands/pick_up_tip.py index c4ddbefb338..8c2902a5f4b 100644 --- a/api/src/opentrons/protocol_engine/commands/pick_up_tip.py +++ b/api/src/opentrons/protocol_engine/commands/pick_up_tip.py @@ -5,7 +5,6 @@ from typing_extensions import Literal from ..types import DeckPoint -from ..errors import TipNotAttachedError from .pipetting_common import ( PipetteIdMixin, WellLocationMixin, From 0b3229be94b765d3badcfb83a83842de90404103 Mon Sep 17 00:00:00 2001 From: Seth Foster Date: Fri, 19 Apr 2024 17:09:12 -0400 Subject: [PATCH 31/41] looks like the editor from the 80s wins again --- robot-server/robot_server/runs/router/commands_router.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/robot-server/robot_server/runs/router/commands_router.py b/robot-server/robot_server/runs/router/commands_router.py index 577c479fb16..15838772412 100644 --- a/robot-server/robot_server/runs/router/commands_router.py +++ b/robot-server/robot_server/runs/router/commands_router.py @@ -138,7 +138,7 @@ async def get_current_run_engine_from_url( You can create a protocol purely over HTTP using protocol commands. If you are running a protocol from a file(s), then you will likely not need to enqueue protocol commands using this endpoint. - + Fixit commands may be enqueued while the run is `awaiting-recovery` state. These commands are intended to fix a failed command. They will be executed right after the failed command From e6ff17310bb67c53801b382839320c5517ffe6ad Mon Sep 17 00:00:00 2001 From: tamarzanzouri Date: Mon, 22 Apr 2024 10:53:15 -0400 Subject: [PATCH 32/41] lint-js --- shared-data/fixture/schemas/1.json | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/shared-data/fixture/schemas/1.json b/shared-data/fixture/schemas/1.json index 647d49a5ac3..73042ab2064 100644 --- a/shared-data/fixture/schemas/1.json +++ b/shared-data/fixture/schemas/1.json @@ -33,12 +33,7 @@ }, "displayCategory": { "type": "string", - "enum": [ - "slot", - "extensionSlot", - "trash", - "other" - ] + "enum": ["slot", "extensionSlot", "trash", "other"] }, "safeString": { "description": "a string safe to use for loadName / namespace. Lowercase-only.", @@ -118,9 +113,7 @@ "type": "object", "description": "Internal describers for the fixture", "additionalProperties": false, - "required": [ - "loadName" - ], + "required": ["loadName"], "properties": { "loadName": { "description": "Name used to reference a fixture definition", From 5877e38d8e2f50c813e4ace69b3c49f257b7de7c Mon Sep 17 00:00:00 2001 From: tamarzanzouri Date: Mon, 22 Apr 2024 11:32:25 -0400 Subject: [PATCH 33/41] router name typo --- .../opentrons/protocol_engine/commands/pick_up_tip.py | 3 +++ .../robot_server/runs/router/commands_router.py | 6 +++--- robot-server/tests/runs/router/test_commands_router.py | 10 +++++----- 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/api/src/opentrons/protocol_engine/commands/pick_up_tip.py b/api/src/opentrons/protocol_engine/commands/pick_up_tip.py index 8c2902a5f4b..ea1e3adfe5e 100644 --- a/api/src/opentrons/protocol_engine/commands/pick_up_tip.py +++ b/api/src/opentrons/protocol_engine/commands/pick_up_tip.py @@ -4,6 +4,7 @@ from typing import TYPE_CHECKING, Optional, Type from typing_extensions import Literal +from ..errors import TipNotAttachedError from ..types import DeckPoint from .pipetting_common import ( PipetteIdMixin, @@ -83,6 +84,8 @@ async def execute(self, params: PickUpTipParams) -> PickUpTipResult: well_name=well_name, ) + raise TipNotAttachedError() + return PickUpTipResult( tipVolume=tip_geometry.volume, tipLength=tip_geometry.length, diff --git a/robot-server/robot_server/runs/router/commands_router.py b/robot-server/robot_server/runs/router/commands_router.py index 15838772412..c290dd1aaf1 100644 --- a/robot-server/robot_server/runs/router/commands_router.py +++ b/robot-server/robot_server/runs/router/commands_router.py @@ -193,7 +193,7 @@ async def create_run_command( " the default was 30 seconds, not infinite." ), ), - failed_command_id: Optional[str] = Query( + failedCommandId: Optional[str] = Query( default=None, description=( "FIXIT command use only. Reference of the failed command id we are trying to fix." @@ -211,7 +211,7 @@ async def create_run_command( Else, return immediately. Comes from a query parameter in the URL. timeout: The maximum time, in seconds, to wait before returning. Comes from a query parameter in the URL. - failed_command_id: FIXIT command use only. + failedCommandId: FIXIT command use only. Reference of the failed command id we are trying to fix. protocol_engine: The run's `ProtocolEngine` on which the new command will be enqueued. @@ -223,7 +223,7 @@ async def create_run_command( command_create = request_body.data.copy(update={"intent": command_intent}) try: command = protocol_engine.add_command( - request=command_create, failed_command_id=failed_command_id + request=command_create, failed_command_id=failedCommandId ) except pe_errors.SetupCommandNotAllowedError as e: diff --git a/robot-server/tests/runs/router/test_commands_router.py b/robot-server/tests/runs/router/test_commands_router.py index 24201aec8cf..93adb46fa53 100644 --- a/robot-server/tests/runs/router/test_commands_router.py +++ b/robot-server/tests/runs/router/test_commands_router.py @@ -126,7 +126,7 @@ def _stub_queued_command_state(*_a: object, **_k: object) -> pe_commands.Command request_body=RequestModelWithCommandCreate(data=command_request), waitUntilComplete=False, protocol_engine=mock_protocol_engine, - failed_command_id=None, + failedCommandId=None, ) assert result.content.data == command_once_added @@ -157,7 +157,7 @@ async def test_create_command_with_failed_command_raises( waitUntilComplete=False, timeout=42, protocol_engine=mock_protocol_engine, - failed_command_id="123", + failedCommandId="123", ) @@ -213,7 +213,7 @@ def _stub_completed_command_state(*_a: object, **_k: object) -> None: waitUntilComplete=True, timeout=999, protocol_engine=mock_protocol_engine, - failed_command_id=None, + failedCommandId=None, ) assert result.content.data == command_once_completed @@ -239,7 +239,7 @@ async def test_add_conflicting_setup_command( request_body=RequestModelWithCommandCreate(data=command_request), waitUntilComplete=False, protocol_engine=mock_protocol_engine, - failed_command_id=None, + failedCommandId=None, ) assert exc_info.value.status_code == 409 @@ -268,7 +268,7 @@ async def test_add_command_to_stopped_engine( request_body=RequestModelWithCommandCreate(data=command_request), waitUntilComplete=False, protocol_engine=mock_protocol_engine, - failed_command_id=None, + failedCommandId=None, ) assert exc_info.value.status_code == 409 From 5da6b3aa093df78c38fc109463d08922e3441935 Mon Sep 17 00:00:00 2001 From: tamarzanzouri Date: Mon, 22 Apr 2024 12:36:05 -0400 Subject: [PATCH 34/41] changed return error type --- .../robot_server/runs/router/commands_router.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/robot-server/robot_server/runs/router/commands_router.py b/robot-server/robot_server/runs/router/commands_router.py index c290dd1aaf1..47a64c5d800 100644 --- a/robot-server/robot_server/runs/router/commands_router.py +++ b/robot-server/robot_server/runs/router/commands_router.py @@ -56,11 +56,18 @@ class CommandNotFound(ErrorDetails): title: str = "Run Command Not Found" +class SetupCommandNotAllowed(ErrorDetails): + """An error if a given run setup command is not allowed.""" + + id: Literal["SetupCommandNotAllowed"] = "SetupCommandNotAllowed" + title: str = "Setup Command Not Allowed" + + class CommandNotAllowed(ErrorDetails): """An error if a given run command is not allowed.""" id: Literal["CommandNotAllowed"] = "CommandNotAllowed" - title: str = "Setup Command Not Allowed" + title: str = "Command Not Allowed" class CommandLinkMeta(BaseModel): @@ -159,8 +166,9 @@ async def get_current_run_engine_from_url( status.HTTP_201_CREATED: {"model": SimpleBody[pe_commands.Command]}, status.HTTP_404_NOT_FOUND: {"model": ErrorBody[RunNotFound]}, status.HTTP_409_CONFLICT: { - "model": ErrorBody[Union[RunStopped, CommandNotAllowed]] + "model": ErrorBody[Union[RunStopped, SetupCommandNotAllowed]] }, + status.HTTP_400_BAD_REQUEST: {"model": ErrorBody[CommandNotAllowed]}, }, ) async def create_run_command( @@ -227,7 +235,7 @@ async def create_run_command( ) except pe_errors.SetupCommandNotAllowedError as e: - raise CommandNotAllowed.from_exc(e).as_error(status.HTTP_409_CONFLICT) + raise SetupCommandNotAllowed.from_exc(e).as_error(status.HTTP_409_CONFLICT) except pe_errors.RunStoppedError as e: raise RunStopped.from_exc(e).as_error(status.HTTP_409_CONFLICT) except pe_errors.CommandNotAllowedError as e: From bd9d0619f82c2b04fe94da08db7604675d3d377b Mon Sep 17 00:00:00 2001 From: tamarzanzouri Date: Mon, 22 Apr 2024 12:59:03 -0400 Subject: [PATCH 35/41] reverted raise for testing --- api/src/opentrons/protocol_engine/commands/pick_up_tip.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/api/src/opentrons/protocol_engine/commands/pick_up_tip.py b/api/src/opentrons/protocol_engine/commands/pick_up_tip.py index ea1e3adfe5e..8c2902a5f4b 100644 --- a/api/src/opentrons/protocol_engine/commands/pick_up_tip.py +++ b/api/src/opentrons/protocol_engine/commands/pick_up_tip.py @@ -4,7 +4,6 @@ from typing import TYPE_CHECKING, Optional, Type from typing_extensions import Literal -from ..errors import TipNotAttachedError from ..types import DeckPoint from .pipetting_common import ( PipetteIdMixin, @@ -84,8 +83,6 @@ async def execute(self, params: PickUpTipParams) -> PickUpTipResult: well_name=well_name, ) - raise TipNotAttachedError() - return PickUpTipResult( tipVolume=tip_geometry.volume, tipLength=tip_geometry.length, From 364c42d40024860fec6fee06aa4800fb19e67588 Mon Sep 17 00:00:00 2001 From: tamarzanzouri Date: Mon, 22 Apr 2024 13:12:23 -0400 Subject: [PATCH 36/41] rename to failedCommandId --- api/src/opentrons/protocol_engine/commands/command.py | 2 +- api/src/opentrons/protocol_engine/state/commands.py | 2 +- api/tests/opentrons/protocol_engine/test_protocol_engine.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/api/src/opentrons/protocol_engine/commands/command.py b/api/src/opentrons/protocol_engine/commands/command.py index 058e9eca275..ad43128236d 100644 --- a/api/src/opentrons/protocol_engine/commands/command.py +++ b/api/src/opentrons/protocol_engine/commands/command.py @@ -160,7 +160,7 @@ class BaseCommand(GenericModel, Generic[CommandParamsT, CommandResultT]): " the command's execution or the command's generation." ), ) - failed_command_id: Optional[str] = Field( + failedCommandId: Optional[str] = Field( None, description=( "FIXIT command use only. Reference of the failed command id we are trying to fix." diff --git a/api/src/opentrons/protocol_engine/state/commands.py b/api/src/opentrons/protocol_engine/state/commands.py index edcc5daf044..f9d7643b728 100644 --- a/api/src/opentrons/protocol_engine/state/commands.py +++ b/api/src/opentrons/protocol_engine/state/commands.py @@ -243,7 +243,7 @@ def handle_action(self, action: Action) -> None: # noqa: C901 params=action.request.params, # type: ignore[arg-type] intent=action.request.intent, status=CommandStatus.QUEUED, - failed_command_id=action.failed_command_id, + failedCommandId=action.failed_command_id, ) self._state.command_history.set_command_queued(queued_command) diff --git a/api/tests/opentrons/protocol_engine/test_protocol_engine.py b/api/tests/opentrons/protocol_engine/test_protocol_engine.py index 2afcaf0f208..4816708fa57 100644 --- a/api/tests/opentrons/protocol_engine/test_protocol_engine.py +++ b/api/tests/opentrons/protocol_engine/test_protocol_engine.py @@ -329,7 +329,7 @@ def test_add_fixit_command_raises( model_utils: ModelUtils, subject: ProtocolEngine, ) -> None: - """It should raise if a failed_command_id is supplied without a fixit command.""" + """It should raise if a failedCommandId is supplied without a fixit command.""" original_request = commands.WaitForResumeCreate( params=commands.WaitForResumeParams() ) From 635d00aea37ed7db1780572ed4dfac1a55a2f9b1 Mon Sep 17 00:00:00 2001 From: TamarZanzouri Date: Mon, 22 Apr 2024 13:54:14 -0400 Subject: [PATCH 37/41] Delete shared-data/fixture/schemas/1.json --- shared-data/fixture/schemas/1.json | 142 ----------------------------- 1 file changed, 142 deletions(-) delete mode 100644 shared-data/fixture/schemas/1.json diff --git a/shared-data/fixture/schemas/1.json b/shared-data/fixture/schemas/1.json deleted file mode 100644 index 73042ab2064..00000000000 --- a/shared-data/fixture/schemas/1.json +++ /dev/null @@ -1,142 +0,0 @@ -{ - "$id": "opentronsFixtureSchemaV1", - "$schema": "http://json-schema.org/draft-07/schema#", - "definitions": { - "positiveNumber": { - "type": "number", - "minimum": 0 - }, - "brandData": { - "type": "object", - "additionalProperties": false, - "required": ["brand"], - "properties": { - "brand": { - "type": "string", - "description": "Brand/manufacturer name" - }, - "brandId": { - "type": "array", - "description": "An array of manufacture numbers pertaining to a given labware", - "items": { - "type": "string" - } - }, - "links": { - "type": "array", - "description": "URLs for manufacturer page(s)", - "items": { - "type": "string" - } - } - } - }, - "displayCategory": { - "type": "string", - "enum": ["slot", "extensionSlot", "trash", "other"] - }, - "safeString": { - "description": "a string safe to use for loadName / namespace. Lowercase-only.", - "type": "string", - "pattern": "^[a-z0-9._]+$" - }, - "coordinates": { - "type": "object", - "additionalProperties": false, - "required": ["x", "y", "z"], - "properties": { - "x": { - "type": "number" - }, - "y": { - "type": "number" - }, - "z": { - "type": "number" - } - } - } - }, - "type": "object", - "additionalProperties": false, - "required": [ - "schemaVersion", - "version", - "namespace", - "metadata", - "brand", - "parameters", - "dimensions" - ], - "properties": { - "schemaVersion": { - "description": "Which schema version a fixture is using", - "type": "number", - "enum": [2] - }, - "version": { - "description": "Version of the fixture definition itself (eg slot v1/v2/v3). An incrementing integer", - "type": "integer", - "minimum": 1 - }, - "namespace": { - "$ref": "#/definitions/safeString" - }, - "metadata": { - "type": "object", - "description": "Properties used for search and display", - "additionalProperties": false, - "required": ["displayName", "displayCategory"], - "properties": { - "displayName": { - "description": "Easy to remember name of labware", - "type": "string" - }, - "displayCategory": { - "$ref": "#/definitions/displayCategory", - "description": "Label(s) used in UI to categorize fixture" - }, - "tags": { - "type": "array", - "description": "List of descriptions for a given fixture", - "items": { - "type": "string" - } - } - } - }, - "brand": { - "$ref": "#/definitions/brandData", - "description": "Real-world fixfure that the definition is modeled from and/or compatible with" - }, - "parameters": { - "type": "object", - "description": "Internal describers for the fixture", - "additionalProperties": false, - "required": ["loadName"], - "properties": { - "loadName": { - "description": "Name used to reference a fixture definition", - "$ref": "#/definitions/safeString" - } - } - }, - "dimensions": { - "type": "object", - "additionalProperties": false, - "description": "Outer dimensions of a fixture", - "required": ["xDimension", "yDimension", "zDimension"], - "properties": { - "yDimension": { - "$ref": "#/definitions/positiveNumber" - }, - "zDimension": { - "$ref": "#/definitions/positiveNumber" - }, - "xDimension": { - "$ref": "#/definitions/positiveNumber" - } - } - } - } -} From 7bbc78bfc34cbdedab1409ec8c4135c2fc99188c Mon Sep 17 00:00:00 2001 From: tamarzanzouri Date: Mon, 22 Apr 2024 14:04:35 -0400 Subject: [PATCH 38/41] fixed failing tests --- .../commands/test_hash_command_params.py | 6 +- .../state/test_command_view_old.py | 4 +- shared-data/command/schemas/8.json | 926 ++++++++++++++---- 3 files changed, 723 insertions(+), 213 deletions(-) diff --git a/api/tests/opentrons/protocol_engine/commands/test_hash_command_params.py b/api/tests/opentrons/protocol_engine/commands/test_hash_command_params.py index fbb6293e7d2..a58fa137bbe 100644 --- a/api/tests/opentrons/protocol_engine/commands/test_hash_command_params.py +++ b/api/tests/opentrons/protocol_engine/commands/test_hash_command_params.py @@ -38,7 +38,7 @@ def test_nonequivalent_commands() -> None: params=commands.BlowOutInPlaceParams( pipetteId="abc123", flowRate=123, - ) + ), intent=CommandIntent.PROTOCOL ) b = commands.WaitForDurationCreate( params=commands.WaitForDurationParams(seconds=123) @@ -52,10 +52,10 @@ def test_nonequivalent_commands() -> None: def test_repeated_commands() -> None: """Repeated commands should hash differently, even though they're equivalent in isolation.""" a = commands.WaitForDurationCreate( - params=commands.WaitForDurationParams(seconds=123) + params=commands.WaitForDurationParams(seconds=123), intent=CommandIntent.PROTOCOL ) b = commands.WaitForDurationCreate( - params=commands.WaitForDurationParams(seconds=123) + params=commands.WaitForDurationParams(seconds=123), intent=CommandIntent.PROTOCOL ) a_hash = hash_protocol_command_params(a, None) diff --git a/api/tests/opentrons/protocol_engine/state/test_command_view_old.py b/api/tests/opentrons/protocol_engine/state/test_command_view_old.py index ddfcd66adb2..19a2515a3e6 100644 --- a/api/tests/opentrons/protocol_engine/state/test_command_view_old.py +++ b/api/tests/opentrons/protocol_engine/state/test_command_view_old.py @@ -145,7 +145,7 @@ def test_get_next_to_execute_returns_first_queued() -> None: @pytest.mark.parametrize( "queue_status", - [QueueStatus.SETUP, QueueStatus.RUNNING, QueueStatus.AWAITING_RECOVERY], + [QueueStatus.SETUP, QueueStatus.RUNNING], ) def test_get_next_to_execute_prioritizes_setup_command_queue( queue_status: QueueStatus, @@ -157,7 +157,7 @@ def test_get_next_to_execute_prioritizes_setup_command_queue( queued_setup_command_ids=["setup-command-id"], ) - assert subject.get_next_to_execute() is None + assert subject.get_next_to_execute() == "setup-command-id" @pytest.mark.parametrize( diff --git a/shared-data/command/schemas/8.json b/shared-data/command/schemas/8.json index a17be9ee690..bae492da17a 100644 --- a/shared-data/command/schemas/8.json +++ b/shared-data/command/schemas/8.json @@ -249,7 +249,11 @@ "WellOrigin": { "title": "WellOrigin", "description": "Origin of WellLocation offset.\n\nProps:\n TOP: the top-center of the well\n BOTTOM: the bottom-center of the well\n CENTER: the middle-center of the well", - "enum": ["top", "bottom", "center"], + "enum": [ + "top", + "bottom", + "center" + ], "type": "string" }, "WellOffset": { @@ -334,12 +338,22 @@ "type": "string" } }, - "required": ["labwareId", "wellName", "flowRate", "volume", "pipetteId"] + "required": [ + "labwareId", + "wellName", + "flowRate", + "volume", + "pipetteId" + ] }, "CommandIntent": { "title": "CommandIntent", "description": "Run intent for a given command.\n\nProps:\n PROTOCOL: the command is part of the protocol run itself.\n SETUP: the command is part of the setup phase of a run.", - "enum": ["protocol", "setup"], + "enum": [ + "protocol", + "setup", + "fixit" + ], "type": "string" }, "AspirateCreate": { @@ -350,7 +364,9 @@ "commandType": { "title": "Commandtype", "default": "aspirate", - "enum": ["aspirate"], + "enum": [ + "aspirate" + ], "type": "string" }, "params": { @@ -370,7 +386,9 @@ "type": "string" } }, - "required": ["params"] + "required": [ + "params" + ] }, "AspirateInPlaceParams": { "title": "AspirateInPlaceParams", @@ -395,7 +413,11 @@ "type": "string" } }, - "required": ["flowRate", "volume", "pipetteId"] + "required": [ + "flowRate", + "volume", + "pipetteId" + ] }, "AspirateInPlaceCreate": { "title": "AspirateInPlaceCreate", @@ -405,7 +427,9 @@ "commandType": { "title": "Commandtype", "default": "aspirateInPlace", - "enum": ["aspirateInPlace"], + "enum": [ + "aspirateInPlace" + ], "type": "string" }, "params": { @@ -425,7 +449,9 @@ "type": "string" } }, - "required": ["params"] + "required": [ + "params" + ] }, "CommentParams": { "title": "CommentParams", @@ -438,7 +464,9 @@ "type": "string" } }, - "required": ["message"] + "required": [ + "message" + ] }, "CommentCreate": { "title": "CommentCreate", @@ -448,7 +476,9 @@ "commandType": { "title": "Commandtype", "default": "comment", - "enum": ["comment"], + "enum": [ + "comment" + ], "type": "string" }, "params": { @@ -468,7 +498,9 @@ "type": "string" } }, - "required": ["params"] + "required": [ + "params" + ] }, "ConfigureForVolumeParams": { "title": "ConfigureForVolumeParams", @@ -487,7 +519,10 @@ "type": "number" } }, - "required": ["pipetteId", "volume"] + "required": [ + "pipetteId", + "volume" + ] }, "ConfigureForVolumeCreate": { "title": "ConfigureForVolumeCreate", @@ -497,7 +532,9 @@ "commandType": { "title": "Commandtype", "default": "configureForVolume", - "enum": ["configureForVolume"], + "enum": [ + "configureForVolume" + ], "type": "string" }, "params": { @@ -517,7 +554,9 @@ "type": "string" } }, - "required": ["params"] + "required": [ + "params" + ] }, "AllNozzleLayoutConfiguration": { "title": "AllNozzleLayoutConfiguration", @@ -527,7 +566,9 @@ "style": { "title": "Style", "default": "ALL", - "enum": ["ALL"], + "enum": [ + "ALL" + ], "type": "string" } } @@ -540,17 +581,26 @@ "style": { "title": "Style", "default": "SINGLE", - "enum": ["SINGLE"], + "enum": [ + "SINGLE" + ], "type": "string" }, "primaryNozzle": { "title": "Primarynozzle", "description": "The primary nozzle to use in the layout configuration. This nozzle will update the critical point of the current pipette. For now, this is also the back left corner of your rectangle.", - "enum": ["A1", "H1", "A12", "H12"], + "enum": [ + "A1", + "H1", + "A12", + "H12" + ], "type": "string" } }, - "required": ["primaryNozzle"] + "required": [ + "primaryNozzle" + ] }, "RowNozzleLayoutConfiguration": { "title": "RowNozzleLayoutConfiguration", @@ -560,17 +610,26 @@ "style": { "title": "Style", "default": "ROW", - "enum": ["ROW"], + "enum": [ + "ROW" + ], "type": "string" }, "primaryNozzle": { "title": "Primarynozzle", "description": "The primary nozzle to use in the layout configuration. This nozzle will update the critical point of the current pipette. For now, this is also the back left corner of your rectangle.", - "enum": ["A1", "H1", "A12", "H12"], + "enum": [ + "A1", + "H1", + "A12", + "H12" + ], "type": "string" } }, - "required": ["primaryNozzle"] + "required": [ + "primaryNozzle" + ] }, "ColumnNozzleLayoutConfiguration": { "title": "ColumnNozzleLayoutConfiguration", @@ -580,17 +639,26 @@ "style": { "title": "Style", "default": "COLUMN", - "enum": ["COLUMN"], + "enum": [ + "COLUMN" + ], "type": "string" }, "primaryNozzle": { "title": "Primarynozzle", "description": "The primary nozzle to use in the layout configuration. This nozzle will update the critical point of the current pipette. For now, this is also the back left corner of your rectangle.", - "enum": ["A1", "H1", "A12", "H12"], + "enum": [ + "A1", + "H1", + "A12", + "H12" + ], "type": "string" } }, - "required": ["primaryNozzle"] + "required": [ + "primaryNozzle" + ] }, "QuadrantNozzleLayoutConfiguration": { "title": "QuadrantNozzleLayoutConfiguration", @@ -600,13 +668,20 @@ "style": { "title": "Style", "default": "QUADRANT", - "enum": ["QUADRANT"], + "enum": [ + "QUADRANT" + ], "type": "string" }, "primaryNozzle": { "title": "Primarynozzle", "description": "The primary nozzle to use in the layout configuration. This nozzle will update the critical point of the current pipette. For now, this is also the back left corner of your rectangle.", - "enum": ["A1", "H1", "A12", "H12"], + "enum": [ + "A1", + "H1", + "A12", + "H12" + ], "type": "string" }, "frontRightNozzle": { @@ -616,7 +691,10 @@ "type": "string" } }, - "required": ["primaryNozzle", "frontRightNozzle"] + "required": [ + "primaryNozzle", + "frontRightNozzle" + ] }, "ConfigureNozzleLayoutParams": { "title": "ConfigureNozzleLayoutParams", @@ -649,7 +727,10 @@ ] } }, - "required": ["pipetteId", "configurationParams"] + "required": [ + "pipetteId", + "configurationParams" + ] }, "ConfigureNozzleLayoutCreate": { "title": "ConfigureNozzleLayoutCreate", @@ -659,7 +740,9 @@ "commandType": { "title": "Commandtype", "default": "configureNozzleLayout", - "enum": ["configureNozzleLayout"], + "enum": [ + "configureNozzleLayout" + ], "type": "string" }, "params": { @@ -679,7 +762,9 @@ "type": "string" } }, - "required": ["params"] + "required": [ + "params" + ] }, "CustomParams": { "title": "CustomParams", @@ -695,7 +780,9 @@ "commandType": { "title": "Commandtype", "default": "custom", - "enum": ["custom"], + "enum": [ + "custom" + ], "type": "string" }, "params": { @@ -715,7 +802,9 @@ "type": "string" } }, - "required": ["params"] + "required": [ + "params" + ] }, "DispenseParams": { "title": "DispenseParams", @@ -764,7 +853,13 @@ "type": "number" } }, - "required": ["labwareId", "wellName", "flowRate", "volume", "pipetteId"] + "required": [ + "labwareId", + "wellName", + "flowRate", + "volume", + "pipetteId" + ] }, "DispenseCreate": { "title": "DispenseCreate", @@ -774,7 +869,9 @@ "commandType": { "title": "Commandtype", "default": "dispense", - "enum": ["dispense"], + "enum": [ + "dispense" + ], "type": "string" }, "params": { @@ -794,7 +891,9 @@ "type": "string" } }, - "required": ["params"] + "required": [ + "params" + ] }, "DispenseInPlaceParams": { "title": "DispenseInPlaceParams", @@ -824,7 +923,11 @@ "type": "number" } }, - "required": ["flowRate", "volume", "pipetteId"] + "required": [ + "flowRate", + "volume", + "pipetteId" + ] }, "DispenseInPlaceCreate": { "title": "DispenseInPlaceCreate", @@ -834,7 +937,9 @@ "commandType": { "title": "Commandtype", "default": "dispenseInPlace", - "enum": ["dispenseInPlace"], + "enum": [ + "dispenseInPlace" + ], "type": "string" }, "params": { @@ -854,7 +959,9 @@ "type": "string" } }, - "required": ["params"] + "required": [ + "params" + ] }, "BlowOutParams": { "title": "BlowOutParams", @@ -892,7 +999,12 @@ "type": "string" } }, - "required": ["labwareId", "wellName", "flowRate", "pipetteId"] + "required": [ + "labwareId", + "wellName", + "flowRate", + "pipetteId" + ] }, "BlowOutCreate": { "title": "BlowOutCreate", @@ -902,7 +1014,9 @@ "commandType": { "title": "Commandtype", "default": "blowout", - "enum": ["blowout"], + "enum": [ + "blowout" + ], "type": "string" }, "params": { @@ -922,7 +1036,9 @@ "type": "string" } }, - "required": ["params"] + "required": [ + "params" + ] }, "BlowOutInPlaceParams": { "title": "BlowOutInPlaceParams", @@ -941,7 +1057,10 @@ "type": "string" } }, - "required": ["flowRate", "pipetteId"] + "required": [ + "flowRate", + "pipetteId" + ] }, "BlowOutInPlaceCreate": { "title": "BlowOutInPlaceCreate", @@ -951,7 +1070,9 @@ "commandType": { "title": "Commandtype", "default": "blowOutInPlace", - "enum": ["blowOutInPlace"], + "enum": [ + "blowOutInPlace" + ], "type": "string" }, "params": { @@ -971,12 +1092,19 @@ "type": "string" } }, - "required": ["params"] + "required": [ + "params" + ] }, "DropTipWellOrigin": { "title": "DropTipWellOrigin", "description": "The origin of a DropTipWellLocation offset.\n\nProps:\n TOP: the top-center of the well\n BOTTOM: the bottom-center of the well\n CENTER: the middle-center of the well\n DEFAULT: the default drop-tip location of the well,\n based on pipette configuration and length of the tip.", - "enum": ["top", "bottom", "center", "default"], + "enum": [ + "top", + "bottom", + "center", + "default" + ], "type": "string" }, "DropTipWellLocation": { @@ -1038,7 +1166,11 @@ "type": "boolean" } }, - "required": ["pipetteId", "labwareId", "wellName"] + "required": [ + "pipetteId", + "labwareId", + "wellName" + ] }, "DropTipCreate": { "title": "DropTipCreate", @@ -1048,7 +1180,9 @@ "commandType": { "title": "Commandtype", "default": "dropTip", - "enum": ["dropTip"], + "enum": [ + "dropTip" + ], "type": "string" }, "params": { @@ -1068,7 +1202,9 @@ "type": "string" } }, - "required": ["params"] + "required": [ + "params" + ] }, "DropTipInPlaceParams": { "title": "DropTipInPlaceParams", @@ -1086,7 +1222,9 @@ "type": "boolean" } }, - "required": ["pipetteId"] + "required": [ + "pipetteId" + ] }, "DropTipInPlaceCreate": { "title": "DropTipInPlaceCreate", @@ -1096,7 +1234,9 @@ "commandType": { "title": "Commandtype", "default": "dropTipInPlace", - "enum": ["dropTipInPlace"], + "enum": [ + "dropTipInPlace" + ], "type": "string" }, "params": { @@ -1116,7 +1256,9 @@ "type": "string" } }, - "required": ["params"] + "required": [ + "params" + ] }, "MotorAxis": { "title": "MotorAxis", @@ -1136,7 +1278,11 @@ "MountType": { "title": "MountType", "description": "An enumeration.", - "enum": ["left", "right", "extension"], + "enum": [ + "left", + "right", + "extension" + ], "type": "string" }, "HomeParams": { @@ -1169,7 +1315,9 @@ "commandType": { "title": "Commandtype", "default": "home", - "enum": ["home"], + "enum": [ + "home" + ], "type": "string" }, "params": { @@ -1189,7 +1337,9 @@ "type": "string" } }, - "required": ["params"] + "required": [ + "params" + ] }, "RetractAxisParams": { "title": "RetractAxisParams", @@ -1205,7 +1355,9 @@ ] } }, - "required": ["axis"] + "required": [ + "axis" + ] }, "RetractAxisCreate": { "title": "RetractAxisCreate", @@ -1215,7 +1367,9 @@ "commandType": { "title": "Commandtype", "default": "retractAxis", - "enum": ["retractAxis"], + "enum": [ + "retractAxis" + ], "type": "string" }, "params": { @@ -1235,7 +1389,9 @@ "type": "string" } }, - "required": ["params"] + "required": [ + "params" + ] }, "DeckSlotName": { "title": "DeckSlotName", @@ -1281,7 +1437,9 @@ ] } }, - "required": ["slotName"] + "required": [ + "slotName" + ] }, "ModuleLocation": { "title": "ModuleLocation", @@ -1294,7 +1452,9 @@ "type": "string" } }, - "required": ["moduleId"] + "required": [ + "moduleId" + ] }, "OnLabwareLocation": { "title": "OnLabwareLocation", @@ -1307,7 +1467,9 @@ "type": "string" } }, - "required": ["labwareId"] + "required": [ + "labwareId" + ] }, "AddressableAreaLocation": { "title": "AddressableAreaLocation", @@ -1320,7 +1482,9 @@ "type": "string" } }, - "required": ["addressableAreaName"] + "required": [ + "addressableAreaName" + ] }, "LoadLabwareParams": { "title": "LoadLabwareParams", @@ -1341,7 +1505,9 @@ "$ref": "#/definitions/OnLabwareLocation" }, { - "enum": ["offDeck"], + "enum": [ + "offDeck" + ], "type": "string" }, { @@ -1375,7 +1541,12 @@ "type": "string" } }, - "required": ["location", "loadName", "namespace", "version"] + "required": [ + "location", + "loadName", + "namespace", + "version" + ] }, "LoadLabwareCreate": { "title": "LoadLabwareCreate", @@ -1385,7 +1556,9 @@ "commandType": { "title": "Commandtype", "default": "loadLabware", - "enum": ["loadLabware"], + "enum": [ + "loadLabware" + ], "type": "string" }, "params": { @@ -1405,7 +1578,9 @@ "type": "string" } }, - "required": ["params"] + "required": [ + "params" + ] }, "LoadLiquidParams": { "title": "LoadLiquidParams", @@ -1431,7 +1606,11 @@ } } }, - "required": ["liquidId", "labwareId", "volumeByWell"] + "required": [ + "liquidId", + "labwareId", + "volumeByWell" + ] }, "LoadLiquidCreate": { "title": "LoadLiquidCreate", @@ -1441,7 +1620,9 @@ "commandType": { "title": "Commandtype", "default": "loadLiquid", - "enum": ["loadLiquid"], + "enum": [ + "loadLiquid" + ], "type": "string" }, "params": { @@ -1461,7 +1642,9 @@ "type": "string" } }, - "required": ["params"] + "required": [ + "params" + ] }, "ModuleModel": { "title": "ModuleModel", @@ -1506,7 +1689,10 @@ "type": "string" } }, - "required": ["model", "location"] + "required": [ + "model", + "location" + ] }, "LoadModuleCreate": { "title": "LoadModuleCreate", @@ -1516,7 +1702,9 @@ "commandType": { "title": "Commandtype", "default": "loadModule", - "enum": ["loadModule"], + "enum": [ + "loadModule" + ], "type": "string" }, "params": { @@ -1536,7 +1724,9 @@ "type": "string" } }, - "required": ["params"] + "required": [ + "params" + ] }, "PipetteNameType": { "title": "PipetteNameType", @@ -1589,7 +1779,10 @@ "type": "string" } }, - "required": ["pipetteName", "mount"] + "required": [ + "pipetteName", + "mount" + ] }, "LoadPipetteCreate": { "title": "LoadPipetteCreate", @@ -1599,7 +1792,9 @@ "commandType": { "title": "Commandtype", "default": "loadPipette", - "enum": ["loadPipette"], + "enum": [ + "loadPipette" + ], "type": "string" }, "params": { @@ -1619,12 +1814,18 @@ "type": "string" } }, - "required": ["params"] + "required": [ + "params" + ] }, "LabwareMovementStrategy": { "title": "LabwareMovementStrategy", "description": "Strategy to use for labware movement.", - "enum": ["usingGripper", "manualMoveWithPause", "manualMoveWithoutPause"], + "enum": [ + "usingGripper", + "manualMoveWithPause", + "manualMoveWithoutPause" + ], "type": "string" }, "LabwareOffsetVector": { @@ -1645,7 +1846,11 @@ "type": "number" } }, - "required": ["x", "y", "z"] + "required": [ + "x", + "y", + "z" + ] }, "MoveLabwareParams": { "title": "MoveLabwareParams", @@ -1671,7 +1876,9 @@ "$ref": "#/definitions/OnLabwareLocation" }, { - "enum": ["offDeck"], + "enum": [ + "offDeck" + ], "type": "string" }, { @@ -1706,7 +1913,11 @@ ] } }, - "required": ["labwareId", "newLocation", "strategy"] + "required": [ + "labwareId", + "newLocation", + "strategy" + ] }, "MoveLabwareCreate": { "title": "MoveLabwareCreate", @@ -1716,7 +1927,9 @@ "commandType": { "title": "Commandtype", "default": "moveLabware", - "enum": ["moveLabware"], + "enum": [ + "moveLabware" + ], "type": "string" }, "params": { @@ -1736,12 +1949,18 @@ "type": "string" } }, - "required": ["params"] + "required": [ + "params" + ] }, "MovementAxis": { "title": "MovementAxis", "description": "Axis on which to issue a relative movement.", - "enum": ["x", "y", "z"], + "enum": [ + "x", + "y", + "z" + ], "type": "string" }, "MoveRelativeParams": { @@ -1768,7 +1987,11 @@ "type": "number" } }, - "required": ["pipetteId", "axis", "distance"] + "required": [ + "pipetteId", + "axis", + "distance" + ] }, "MoveRelativeCreate": { "title": "MoveRelativeCreate", @@ -1778,7 +2001,9 @@ "commandType": { "title": "Commandtype", "default": "moveRelative", - "enum": ["moveRelative"], + "enum": [ + "moveRelative" + ], "type": "string" }, "params": { @@ -1798,7 +2023,9 @@ "type": "string" } }, - "required": ["params"] + "required": [ + "params" + ] }, "DeckPoint": { "title": "DeckPoint", @@ -1818,7 +2045,11 @@ "type": "number" } }, - "required": ["x", "y", "z"] + "required": [ + "x", + "y", + "z" + ] }, "MoveToCoordinatesParams": { "title": "MoveToCoordinatesParams", @@ -1856,7 +2087,10 @@ ] } }, - "required": ["pipetteId", "coordinates"] + "required": [ + "pipetteId", + "coordinates" + ] }, "MoveToCoordinatesCreate": { "title": "MoveToCoordinatesCreate", @@ -1866,7 +2100,9 @@ "commandType": { "title": "Commandtype", "default": "moveToCoordinates", - "enum": ["moveToCoordinates"], + "enum": [ + "moveToCoordinates" + ], "type": "string" }, "params": { @@ -1886,7 +2122,9 @@ "type": "string" } }, - "required": ["params"] + "required": [ + "params" + ] }, "MoveToWellParams": { "title": "MoveToWellParams", @@ -1934,7 +2172,11 @@ "type": "string" } }, - "required": ["labwareId", "wellName", "pipetteId"] + "required": [ + "labwareId", + "wellName", + "pipetteId" + ] }, "MoveToWellCreate": { "title": "MoveToWellCreate", @@ -1944,7 +2186,9 @@ "commandType": { "title": "Commandtype", "default": "moveToWell", - "enum": ["moveToWell"], + "enum": [ + "moveToWell" + ], "type": "string" }, "params": { @@ -1964,7 +2208,9 @@ "type": "string" } }, - "required": ["params"] + "required": [ + "params" + ] }, "AddressableOffsetVector": { "title": "AddressableOffsetVector", @@ -1984,7 +2230,11 @@ "type": "number" } }, - "required": ["x", "y", "z"] + "required": [ + "x", + "y", + "z" + ] }, "MoveToAddressableAreaParams": { "title": "MoveToAddressableAreaParams", @@ -2038,7 +2288,10 @@ "type": "boolean" } }, - "required": ["pipetteId", "addressableAreaName"] + "required": [ + "pipetteId", + "addressableAreaName" + ] }, "MoveToAddressableAreaCreate": { "title": "MoveToAddressableAreaCreate", @@ -2048,7 +2301,9 @@ "commandType": { "title": "Commandtype", "default": "moveToAddressableArea", - "enum": ["moveToAddressableArea"], + "enum": [ + "moveToAddressableArea" + ], "type": "string" }, "params": { @@ -2068,7 +2323,9 @@ "type": "string" } }, - "required": ["params"] + "required": [ + "params" + ] }, "MoveToAddressableAreaForDropTipParams": { "title": "MoveToAddressableAreaForDropTipParams", @@ -2128,7 +2385,10 @@ "type": "boolean" } }, - "required": ["pipetteId", "addressableAreaName"] + "required": [ + "pipetteId", + "addressableAreaName" + ] }, "MoveToAddressableAreaForDropTipCreate": { "title": "MoveToAddressableAreaForDropTipCreate", @@ -2138,7 +2398,9 @@ "commandType": { "title": "Commandtype", "default": "moveToAddressableAreaForDropTip", - "enum": ["moveToAddressableAreaForDropTip"], + "enum": [ + "moveToAddressableAreaForDropTip" + ], "type": "string" }, "params": { @@ -2158,7 +2420,9 @@ "type": "string" } }, - "required": ["params"] + "required": [ + "params" + ] }, "PrepareToAspirateParams": { "title": "PrepareToAspirateParams", @@ -2171,7 +2435,9 @@ "type": "string" } }, - "required": ["pipetteId"] + "required": [ + "pipetteId" + ] }, "PrepareToAspirateCreate": { "title": "PrepareToAspirateCreate", @@ -2181,7 +2447,9 @@ "commandType": { "title": "Commandtype", "default": "prepareToAspirate", - "enum": ["prepareToAspirate"], + "enum": [ + "prepareToAspirate" + ], "type": "string" }, "params": { @@ -2201,7 +2469,9 @@ "type": "string" } }, - "required": ["params"] + "required": [ + "params" + ] }, "WaitForResumeParams": { "title": "WaitForResumeParams", @@ -2223,7 +2493,10 @@ "commandType": { "title": "Commandtype", "default": "waitForResume", - "enum": ["waitForResume", "pause"], + "enum": [ + "waitForResume", + "pause" + ], "type": "string" }, "params": { @@ -2243,7 +2516,9 @@ "type": "string" } }, - "required": ["params"] + "required": [ + "params" + ] }, "WaitForDurationParams": { "title": "WaitForDurationParams", @@ -2261,7 +2536,9 @@ "type": "string" } }, - "required": ["seconds"] + "required": [ + "seconds" + ] }, "WaitForDurationCreate": { "title": "WaitForDurationCreate", @@ -2271,7 +2548,9 @@ "commandType": { "title": "Commandtype", "default": "waitForDuration", - "enum": ["waitForDuration"], + "enum": [ + "waitForDuration" + ], "type": "string" }, "params": { @@ -2291,7 +2570,9 @@ "type": "string" } }, - "required": ["params"] + "required": [ + "params" + ] }, "PickUpTipParams": { "title": "PickUpTipParams", @@ -2323,7 +2604,11 @@ "type": "string" } }, - "required": ["labwareId", "wellName", "pipetteId"] + "required": [ + "labwareId", + "wellName", + "pipetteId" + ] }, "PickUpTipCreate": { "title": "PickUpTipCreate", @@ -2333,7 +2618,9 @@ "commandType": { "title": "Commandtype", "default": "pickUpTip", - "enum": ["pickUpTip"], + "enum": [ + "pickUpTip" + ], "type": "string" }, "params": { @@ -2353,7 +2640,9 @@ "type": "string" } }, - "required": ["params"] + "required": [ + "params" + ] }, "SavePositionParams": { "title": "SavePositionParams", @@ -2377,7 +2666,9 @@ "type": "boolean" } }, - "required": ["pipetteId"] + "required": [ + "pipetteId" + ] }, "SavePositionCreate": { "title": "SavePositionCreate", @@ -2387,7 +2678,9 @@ "commandType": { "title": "Commandtype", "default": "savePosition", - "enum": ["savePosition"], + "enum": [ + "savePosition" + ], "type": "string" }, "params": { @@ -2407,7 +2700,9 @@ "type": "string" } }, - "required": ["params"] + "required": [ + "params" + ] }, "SetRailLightsParams": { "title": "SetRailLightsParams", @@ -2420,7 +2715,9 @@ "type": "boolean" } }, - "required": ["on"] + "required": [ + "on" + ] }, "SetRailLightsCreate": { "title": "SetRailLightsCreate", @@ -2430,7 +2727,9 @@ "commandType": { "title": "Commandtype", "default": "setRailLights", - "enum": ["setRailLights"], + "enum": [ + "setRailLights" + ], "type": "string" }, "params": { @@ -2450,7 +2749,9 @@ "type": "string" } }, - "required": ["params"] + "required": [ + "params" + ] }, "TouchTipParams": { "title": "TouchTipParams", @@ -2493,7 +2794,11 @@ "type": "number" } }, - "required": ["labwareId", "wellName", "pipetteId"] + "required": [ + "labwareId", + "wellName", + "pipetteId" + ] }, "TouchTipCreate": { "title": "TouchTipCreate", @@ -2503,7 +2808,9 @@ "commandType": { "title": "Commandtype", "default": "touchTip", - "enum": ["touchTip"], + "enum": [ + "touchTip" + ], "type": "string" }, "params": { @@ -2523,12 +2830,20 @@ "type": "string" } }, - "required": ["params"] + "required": [ + "params" + ] }, "StatusBarAnimation": { "title": "StatusBarAnimation", "description": "Status Bar animation options.", - "enum": ["idle", "confirm", "updating", "disco", "off"] + "enum": [ + "idle", + "confirm", + "updating", + "disco", + "off" + ] }, "SetStatusBarParams": { "title": "SetStatusBarParams", @@ -2544,7 +2859,9 @@ ] } }, - "required": ["animation"] + "required": [ + "animation" + ] }, "SetStatusBarCreate": { "title": "SetStatusBarCreate", @@ -2554,7 +2871,9 @@ "commandType": { "title": "Commandtype", "default": "setStatusBar", - "enum": ["setStatusBar"], + "enum": [ + "setStatusBar" + ], "type": "string" }, "params": { @@ -2574,12 +2893,18 @@ "type": "string" } }, - "required": ["params"] + "required": [ + "params" + ] }, "TipPresenceStatus": { "title": "TipPresenceStatus", "description": "Tip presence status reported by a pipette.", - "enum": ["present", "absent", "unknown"], + "enum": [ + "present", + "absent", + "unknown" + ], "type": "string" }, "VerifyTipPresenceParams": { @@ -2601,7 +2926,10 @@ ] } }, - "required": ["pipetteId", "expectedState"] + "required": [ + "pipetteId", + "expectedState" + ] }, "VerifyTipPresenceCreate": { "title": "VerifyTipPresenceCreate", @@ -2611,7 +2939,9 @@ "commandType": { "title": "Commandtype", "default": "verifyTipPresence", - "enum": ["verifyTipPresence"], + "enum": [ + "verifyTipPresence" + ], "type": "string" }, "params": { @@ -2631,7 +2961,9 @@ "type": "string" } }, - "required": ["params"] + "required": [ + "params" + ] }, "GetTipPresenceParams": { "title": "GetTipPresenceParams", @@ -2644,7 +2976,9 @@ "type": "string" } }, - "required": ["pipetteId"] + "required": [ + "pipetteId" + ] }, "GetTipPresenceCreate": { "title": "GetTipPresenceCreate", @@ -2654,7 +2988,9 @@ "commandType": { "title": "Commandtype", "default": "getTipPresence", - "enum": ["getTipPresence"], + "enum": [ + "getTipPresence" + ], "type": "string" }, "params": { @@ -2674,7 +3010,9 @@ "type": "string" } }, - "required": ["params"] + "required": [ + "params" + ] }, "opentrons__protocol_engine__commands__heater_shaker__wait_for_temperature__WaitForTemperatureParams": { "title": "WaitForTemperatureParams", @@ -2692,7 +3030,9 @@ "type": "number" } }, - "required": ["moduleId"] + "required": [ + "moduleId" + ] }, "opentrons__protocol_engine__commands__heater_shaker__wait_for_temperature__WaitForTemperatureCreate": { "title": "WaitForTemperatureCreate", @@ -2702,7 +3042,9 @@ "commandType": { "title": "Commandtype", "default": "heaterShaker/waitForTemperature", - "enum": ["heaterShaker/waitForTemperature"], + "enum": [ + "heaterShaker/waitForTemperature" + ], "type": "string" }, "params": { @@ -2722,7 +3064,9 @@ "type": "string" } }, - "required": ["params"] + "required": [ + "params" + ] }, "opentrons__protocol_engine__commands__heater_shaker__set_target_temperature__SetTargetTemperatureParams": { "title": "SetTargetTemperatureParams", @@ -2740,7 +3084,10 @@ "type": "number" } }, - "required": ["moduleId", "celsius"] + "required": [ + "moduleId", + "celsius" + ] }, "opentrons__protocol_engine__commands__heater_shaker__set_target_temperature__SetTargetTemperatureCreate": { "title": "SetTargetTemperatureCreate", @@ -2750,7 +3097,9 @@ "commandType": { "title": "Commandtype", "default": "heaterShaker/setTargetTemperature", - "enum": ["heaterShaker/setTargetTemperature"], + "enum": [ + "heaterShaker/setTargetTemperature" + ], "type": "string" }, "params": { @@ -2770,7 +3119,9 @@ "type": "string" } }, - "required": ["params"] + "required": [ + "params" + ] }, "DeactivateHeaterParams": { "title": "DeactivateHeaterParams", @@ -2783,7 +3134,9 @@ "type": "string" } }, - "required": ["moduleId"] + "required": [ + "moduleId" + ] }, "DeactivateHeaterCreate": { "title": "DeactivateHeaterCreate", @@ -2793,7 +3146,9 @@ "commandType": { "title": "Commandtype", "default": "heaterShaker/deactivateHeater", - "enum": ["heaterShaker/deactivateHeater"], + "enum": [ + "heaterShaker/deactivateHeater" + ], "type": "string" }, "params": { @@ -2813,7 +3168,9 @@ "type": "string" } }, - "required": ["params"] + "required": [ + "params" + ] }, "SetAndWaitForShakeSpeedParams": { "title": "SetAndWaitForShakeSpeedParams", @@ -2831,7 +3188,10 @@ "type": "number" } }, - "required": ["moduleId", "rpm"] + "required": [ + "moduleId", + "rpm" + ] }, "SetAndWaitForShakeSpeedCreate": { "title": "SetAndWaitForShakeSpeedCreate", @@ -2841,7 +3201,9 @@ "commandType": { "title": "Commandtype", "default": "heaterShaker/setAndWaitForShakeSpeed", - "enum": ["heaterShaker/setAndWaitForShakeSpeed"], + "enum": [ + "heaterShaker/setAndWaitForShakeSpeed" + ], "type": "string" }, "params": { @@ -2861,7 +3223,9 @@ "type": "string" } }, - "required": ["params"] + "required": [ + "params" + ] }, "DeactivateShakerParams": { "title": "DeactivateShakerParams", @@ -2874,7 +3238,9 @@ "type": "string" } }, - "required": ["moduleId"] + "required": [ + "moduleId" + ] }, "DeactivateShakerCreate": { "title": "DeactivateShakerCreate", @@ -2884,7 +3250,9 @@ "commandType": { "title": "Commandtype", "default": "heaterShaker/deactivateShaker", - "enum": ["heaterShaker/deactivateShaker"], + "enum": [ + "heaterShaker/deactivateShaker" + ], "type": "string" }, "params": { @@ -2904,7 +3272,9 @@ "type": "string" } }, - "required": ["params"] + "required": [ + "params" + ] }, "OpenLabwareLatchParams": { "title": "OpenLabwareLatchParams", @@ -2917,7 +3287,9 @@ "type": "string" } }, - "required": ["moduleId"] + "required": [ + "moduleId" + ] }, "OpenLabwareLatchCreate": { "title": "OpenLabwareLatchCreate", @@ -2927,7 +3299,9 @@ "commandType": { "title": "Commandtype", "default": "heaterShaker/openLabwareLatch", - "enum": ["heaterShaker/openLabwareLatch"], + "enum": [ + "heaterShaker/openLabwareLatch" + ], "type": "string" }, "params": { @@ -2947,7 +3321,9 @@ "type": "string" } }, - "required": ["params"] + "required": [ + "params" + ] }, "CloseLabwareLatchParams": { "title": "CloseLabwareLatchParams", @@ -2960,7 +3336,9 @@ "type": "string" } }, - "required": ["moduleId"] + "required": [ + "moduleId" + ] }, "CloseLabwareLatchCreate": { "title": "CloseLabwareLatchCreate", @@ -2970,7 +3348,9 @@ "commandType": { "title": "Commandtype", "default": "heaterShaker/closeLabwareLatch", - "enum": ["heaterShaker/closeLabwareLatch"], + "enum": [ + "heaterShaker/closeLabwareLatch" + ], "type": "string" }, "params": { @@ -2990,7 +3370,9 @@ "type": "string" } }, - "required": ["params"] + "required": [ + "params" + ] }, "DisengageParams": { "title": "DisengageParams", @@ -3003,7 +3385,9 @@ "type": "string" } }, - "required": ["moduleId"] + "required": [ + "moduleId" + ] }, "DisengageCreate": { "title": "DisengageCreate", @@ -3013,7 +3397,9 @@ "commandType": { "title": "Commandtype", "default": "magneticModule/disengage", - "enum": ["magneticModule/disengage"], + "enum": [ + "magneticModule/disengage" + ], "type": "string" }, "params": { @@ -3033,7 +3419,9 @@ "type": "string" } }, - "required": ["params"] + "required": [ + "params" + ] }, "EngageParams": { "title": "EngageParams", @@ -3051,7 +3439,10 @@ "type": "number" } }, - "required": ["moduleId", "height"] + "required": [ + "moduleId", + "height" + ] }, "EngageCreate": { "title": "EngageCreate", @@ -3061,7 +3452,9 @@ "commandType": { "title": "Commandtype", "default": "magneticModule/engage", - "enum": ["magneticModule/engage"], + "enum": [ + "magneticModule/engage" + ], "type": "string" }, "params": { @@ -3081,7 +3474,9 @@ "type": "string" } }, - "required": ["params"] + "required": [ + "params" + ] }, "opentrons__protocol_engine__commands__temperature_module__set_target_temperature__SetTargetTemperatureParams": { "title": "SetTargetTemperatureParams", @@ -3099,7 +3494,10 @@ "type": "number" } }, - "required": ["moduleId", "celsius"] + "required": [ + "moduleId", + "celsius" + ] }, "opentrons__protocol_engine__commands__temperature_module__set_target_temperature__SetTargetTemperatureCreate": { "title": "SetTargetTemperatureCreate", @@ -3109,7 +3507,9 @@ "commandType": { "title": "Commandtype", "default": "temperatureModule/setTargetTemperature", - "enum": ["temperatureModule/setTargetTemperature"], + "enum": [ + "temperatureModule/setTargetTemperature" + ], "type": "string" }, "params": { @@ -3129,7 +3529,9 @@ "type": "string" } }, - "required": ["params"] + "required": [ + "params" + ] }, "opentrons__protocol_engine__commands__temperature_module__wait_for_temperature__WaitForTemperatureParams": { "title": "WaitForTemperatureParams", @@ -3147,7 +3549,9 @@ "type": "number" } }, - "required": ["moduleId"] + "required": [ + "moduleId" + ] }, "opentrons__protocol_engine__commands__temperature_module__wait_for_temperature__WaitForTemperatureCreate": { "title": "WaitForTemperatureCreate", @@ -3157,7 +3561,9 @@ "commandType": { "title": "Commandtype", "default": "temperatureModule/waitForTemperature", - "enum": ["temperatureModule/waitForTemperature"], + "enum": [ + "temperatureModule/waitForTemperature" + ], "type": "string" }, "params": { @@ -3177,7 +3583,9 @@ "type": "string" } }, - "required": ["params"] + "required": [ + "params" + ] }, "DeactivateTemperatureParams": { "title": "DeactivateTemperatureParams", @@ -3190,7 +3598,9 @@ "type": "string" } }, - "required": ["moduleId"] + "required": [ + "moduleId" + ] }, "DeactivateTemperatureCreate": { "title": "DeactivateTemperatureCreate", @@ -3200,7 +3610,9 @@ "commandType": { "title": "Commandtype", "default": "temperatureModule/deactivate", - "enum": ["temperatureModule/deactivate"], + "enum": [ + "temperatureModule/deactivate" + ], "type": "string" }, "params": { @@ -3220,7 +3632,9 @@ "type": "string" } }, - "required": ["params"] + "required": [ + "params" + ] }, "SetTargetBlockTemperatureParams": { "title": "SetTargetBlockTemperatureParams", @@ -3248,7 +3662,10 @@ "type": "number" } }, - "required": ["moduleId", "celsius"] + "required": [ + "moduleId", + "celsius" + ] }, "SetTargetBlockTemperatureCreate": { "title": "SetTargetBlockTemperatureCreate", @@ -3258,7 +3675,9 @@ "commandType": { "title": "Commandtype", "default": "thermocycler/setTargetBlockTemperature", - "enum": ["thermocycler/setTargetBlockTemperature"], + "enum": [ + "thermocycler/setTargetBlockTemperature" + ], "type": "string" }, "params": { @@ -3278,7 +3697,9 @@ "type": "string" } }, - "required": ["params"] + "required": [ + "params" + ] }, "WaitForBlockTemperatureParams": { "title": "WaitForBlockTemperatureParams", @@ -3291,7 +3712,9 @@ "type": "string" } }, - "required": ["moduleId"] + "required": [ + "moduleId" + ] }, "WaitForBlockTemperatureCreate": { "title": "WaitForBlockTemperatureCreate", @@ -3301,7 +3724,9 @@ "commandType": { "title": "Commandtype", "default": "thermocycler/waitForBlockTemperature", - "enum": ["thermocycler/waitForBlockTemperature"], + "enum": [ + "thermocycler/waitForBlockTemperature" + ], "type": "string" }, "params": { @@ -3321,7 +3746,9 @@ "type": "string" } }, - "required": ["params"] + "required": [ + "params" + ] }, "SetTargetLidTemperatureParams": { "title": "SetTargetLidTemperatureParams", @@ -3339,7 +3766,10 @@ "type": "number" } }, - "required": ["moduleId", "celsius"] + "required": [ + "moduleId", + "celsius" + ] }, "SetTargetLidTemperatureCreate": { "title": "SetTargetLidTemperatureCreate", @@ -3349,7 +3779,9 @@ "commandType": { "title": "Commandtype", "default": "thermocycler/setTargetLidTemperature", - "enum": ["thermocycler/setTargetLidTemperature"], + "enum": [ + "thermocycler/setTargetLidTemperature" + ], "type": "string" }, "params": { @@ -3369,7 +3801,9 @@ "type": "string" } }, - "required": ["params"] + "required": [ + "params" + ] }, "WaitForLidTemperatureParams": { "title": "WaitForLidTemperatureParams", @@ -3382,7 +3816,9 @@ "type": "string" } }, - "required": ["moduleId"] + "required": [ + "moduleId" + ] }, "WaitForLidTemperatureCreate": { "title": "WaitForLidTemperatureCreate", @@ -3392,7 +3828,9 @@ "commandType": { "title": "Commandtype", "default": "thermocycler/waitForLidTemperature", - "enum": ["thermocycler/waitForLidTemperature"], + "enum": [ + "thermocycler/waitForLidTemperature" + ], "type": "string" }, "params": { @@ -3412,7 +3850,9 @@ "type": "string" } }, - "required": ["params"] + "required": [ + "params" + ] }, "DeactivateBlockParams": { "title": "DeactivateBlockParams", @@ -3425,7 +3865,9 @@ "type": "string" } }, - "required": ["moduleId"] + "required": [ + "moduleId" + ] }, "DeactivateBlockCreate": { "title": "DeactivateBlockCreate", @@ -3435,7 +3877,9 @@ "commandType": { "title": "Commandtype", "default": "thermocycler/deactivateBlock", - "enum": ["thermocycler/deactivateBlock"], + "enum": [ + "thermocycler/deactivateBlock" + ], "type": "string" }, "params": { @@ -3455,7 +3899,9 @@ "type": "string" } }, - "required": ["params"] + "required": [ + "params" + ] }, "DeactivateLidParams": { "title": "DeactivateLidParams", @@ -3468,7 +3914,9 @@ "type": "string" } }, - "required": ["moduleId"] + "required": [ + "moduleId" + ] }, "DeactivateLidCreate": { "title": "DeactivateLidCreate", @@ -3478,7 +3926,9 @@ "commandType": { "title": "Commandtype", "default": "thermocycler/deactivateLid", - "enum": ["thermocycler/deactivateLid"], + "enum": [ + "thermocycler/deactivateLid" + ], "type": "string" }, "params": { @@ -3498,7 +3948,9 @@ "type": "string" } }, - "required": ["params"] + "required": [ + "params" + ] }, "OpenLidParams": { "title": "OpenLidParams", @@ -3511,7 +3963,9 @@ "type": "string" } }, - "required": ["moduleId"] + "required": [ + "moduleId" + ] }, "OpenLidCreate": { "title": "OpenLidCreate", @@ -3521,7 +3975,9 @@ "commandType": { "title": "Commandtype", "default": "thermocycler/openLid", - "enum": ["thermocycler/openLid"], + "enum": [ + "thermocycler/openLid" + ], "type": "string" }, "params": { @@ -3541,7 +3997,9 @@ "type": "string" } }, - "required": ["params"] + "required": [ + "params" + ] }, "CloseLidParams": { "title": "CloseLidParams", @@ -3554,7 +4012,9 @@ "type": "string" } }, - "required": ["moduleId"] + "required": [ + "moduleId" + ] }, "CloseLidCreate": { "title": "CloseLidCreate", @@ -3564,7 +4024,9 @@ "commandType": { "title": "Commandtype", "default": "thermocycler/closeLid", - "enum": ["thermocycler/closeLid"], + "enum": [ + "thermocycler/closeLid" + ], "type": "string" }, "params": { @@ -3584,7 +4046,9 @@ "type": "string" } }, - "required": ["params"] + "required": [ + "params" + ] }, "RunProfileStepParams": { "title": "RunProfileStepParams", @@ -3602,7 +4066,10 @@ "type": "number" } }, - "required": ["celsius", "holdSeconds"] + "required": [ + "celsius", + "holdSeconds" + ] }, "RunProfileParams": { "title": "RunProfileParams", @@ -3628,7 +4095,10 @@ "type": "number" } }, - "required": ["moduleId", "profile"] + "required": [ + "moduleId", + "profile" + ] }, "RunProfileCreate": { "title": "RunProfileCreate", @@ -3638,7 +4108,9 @@ "commandType": { "title": "Commandtype", "default": "thermocycler/runProfile", - "enum": ["thermocycler/runProfile"], + "enum": [ + "thermocycler/runProfile" + ], "type": "string" }, "params": { @@ -3658,12 +4130,17 @@ "type": "string" } }, - "required": ["params"] + "required": [ + "params" + ] }, "CalibrateGripperParamsJaw": { "title": "CalibrateGripperParamsJaw", "description": "An enumeration.", - "enum": ["front", "rear"] + "enum": [ + "front", + "rear" + ] }, "Vec3f": { "title": "Vec3f", @@ -3683,7 +4160,11 @@ "type": "number" } }, - "required": ["x", "y", "z"] + "required": [ + "x", + "y", + "z" + ] }, "CalibrateGripperParams": { "title": "CalibrateGripperParams", @@ -3708,7 +4189,9 @@ ] } }, - "required": ["jaw"] + "required": [ + "jaw" + ] }, "CalibrateGripperCreate": { "title": "CalibrateGripperCreate", @@ -3718,7 +4201,9 @@ "commandType": { "title": "Commandtype", "default": "calibration/calibrateGripper", - "enum": ["calibration/calibrateGripper"], + "enum": [ + "calibration/calibrateGripper" + ], "type": "string" }, "params": { @@ -3738,7 +4223,9 @@ "type": "string" } }, - "required": ["params"] + "required": [ + "params" + ] }, "CalibratePipetteParams": { "title": "CalibratePipetteParams", @@ -3754,7 +4241,9 @@ ] } }, - "required": ["mount"] + "required": [ + "mount" + ] }, "CalibratePipetteCreate": { "title": "CalibratePipetteCreate", @@ -3764,7 +4253,9 @@ "commandType": { "title": "Commandtype", "default": "calibration/calibratePipette", - "enum": ["calibration/calibratePipette"], + "enum": [ + "calibration/calibratePipette" + ], "type": "string" }, "params": { @@ -3784,7 +4275,9 @@ "type": "string" } }, - "required": ["params"] + "required": [ + "params" + ] }, "CalibrateModuleParams": { "title": "CalibrateModuleParams", @@ -3810,7 +4303,11 @@ ] } }, - "required": ["moduleId", "labwareId", "mount"] + "required": [ + "moduleId", + "labwareId", + "mount" + ] }, "CalibrateModuleCreate": { "title": "CalibrateModuleCreate", @@ -3820,7 +4317,9 @@ "commandType": { "title": "Commandtype", "default": "calibration/calibrateModule", - "enum": ["calibration/calibrateModule"], + "enum": [ + "calibration/calibrateModule" + ], "type": "string" }, "params": { @@ -3840,12 +4339,17 @@ "type": "string" } }, - "required": ["params"] + "required": [ + "params" + ] }, "MaintenancePosition": { "title": "MaintenancePosition", "description": "Maintenance position options.", - "enum": ["attachPlate", "attachInstrument"] + "enum": [ + "attachPlate", + "attachInstrument" + ] }, "MoveToMaintenancePositionParams": { "title": "MoveToMaintenancePositionParams", @@ -3870,7 +4374,9 @@ ] } }, - "required": ["mount"] + "required": [ + "mount" + ] }, "MoveToMaintenancePositionCreate": { "title": "MoveToMaintenancePositionCreate", @@ -3880,7 +4386,9 @@ "commandType": { "title": "Commandtype", "default": "calibration/moveToMaintenancePosition", - "enum": ["calibration/moveToMaintenancePosition"], + "enum": [ + "calibration/moveToMaintenancePosition" + ], "type": "string" }, "params": { @@ -3900,7 +4408,9 @@ "type": "string" } }, - "required": ["params"] + "required": [ + "params" + ] } }, "$id": "opentronsCommandSchemaV8", From b2ed3cf98057a0d5bdc539ef80fad4d660ce07ba Mon Sep 17 00:00:00 2001 From: tamarzanzouri Date: Mon, 22 Apr 2024 14:35:08 -0400 Subject: [PATCH 39/41] lint --- .../protocol_engine/commands/test_hash_command_params.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/api/tests/opentrons/protocol_engine/commands/test_hash_command_params.py b/api/tests/opentrons/protocol_engine/commands/test_hash_command_params.py index a58fa137bbe..9988854a9d4 100644 --- a/api/tests/opentrons/protocol_engine/commands/test_hash_command_params.py +++ b/api/tests/opentrons/protocol_engine/commands/test_hash_command_params.py @@ -38,7 +38,8 @@ def test_nonequivalent_commands() -> None: params=commands.BlowOutInPlaceParams( pipetteId="abc123", flowRate=123, - ), intent=CommandIntent.PROTOCOL + ), + intent=CommandIntent.PROTOCOL, ) b = commands.WaitForDurationCreate( params=commands.WaitForDurationParams(seconds=123) @@ -52,10 +53,12 @@ def test_nonequivalent_commands() -> None: def test_repeated_commands() -> None: """Repeated commands should hash differently, even though they're equivalent in isolation.""" a = commands.WaitForDurationCreate( - params=commands.WaitForDurationParams(seconds=123), intent=CommandIntent.PROTOCOL + params=commands.WaitForDurationParams(seconds=123), + intent=CommandIntent.PROTOCOL, ) b = commands.WaitForDurationCreate( - params=commands.WaitForDurationParams(seconds=123), intent=CommandIntent.PROTOCOL + params=commands.WaitForDurationParams(seconds=123), + intent=CommandIntent.PROTOCOL, ) a_hash = hash_protocol_command_params(a, None) From 057f9741236ad338e3e3895d5eb4540902451b74 Mon Sep 17 00:00:00 2001 From: tamarzanzouri Date: Mon, 22 Apr 2024 14:42:31 -0400 Subject: [PATCH 40/41] format-js --- shared-data/command/schemas/8.json | 926 +++++++---------------------- 1 file changed, 208 insertions(+), 718 deletions(-) diff --git a/shared-data/command/schemas/8.json b/shared-data/command/schemas/8.json index bae492da17a..f3c5bb38b27 100644 --- a/shared-data/command/schemas/8.json +++ b/shared-data/command/schemas/8.json @@ -249,11 +249,7 @@ "WellOrigin": { "title": "WellOrigin", "description": "Origin of WellLocation offset.\n\nProps:\n TOP: the top-center of the well\n BOTTOM: the bottom-center of the well\n CENTER: the middle-center of the well", - "enum": [ - "top", - "bottom", - "center" - ], + "enum": ["top", "bottom", "center"], "type": "string" }, "WellOffset": { @@ -338,22 +334,12 @@ "type": "string" } }, - "required": [ - "labwareId", - "wellName", - "flowRate", - "volume", - "pipetteId" - ] + "required": ["labwareId", "wellName", "flowRate", "volume", "pipetteId"] }, "CommandIntent": { "title": "CommandIntent", "description": "Run intent for a given command.\n\nProps:\n PROTOCOL: the command is part of the protocol run itself.\n SETUP: the command is part of the setup phase of a run.", - "enum": [ - "protocol", - "setup", - "fixit" - ], + "enum": ["protocol", "setup", "fixit"], "type": "string" }, "AspirateCreate": { @@ -364,9 +350,7 @@ "commandType": { "title": "Commandtype", "default": "aspirate", - "enum": [ - "aspirate" - ], + "enum": ["aspirate"], "type": "string" }, "params": { @@ -386,9 +370,7 @@ "type": "string" } }, - "required": [ - "params" - ] + "required": ["params"] }, "AspirateInPlaceParams": { "title": "AspirateInPlaceParams", @@ -413,11 +395,7 @@ "type": "string" } }, - "required": [ - "flowRate", - "volume", - "pipetteId" - ] + "required": ["flowRate", "volume", "pipetteId"] }, "AspirateInPlaceCreate": { "title": "AspirateInPlaceCreate", @@ -427,9 +405,7 @@ "commandType": { "title": "Commandtype", "default": "aspirateInPlace", - "enum": [ - "aspirateInPlace" - ], + "enum": ["aspirateInPlace"], "type": "string" }, "params": { @@ -449,9 +425,7 @@ "type": "string" } }, - "required": [ - "params" - ] + "required": ["params"] }, "CommentParams": { "title": "CommentParams", @@ -464,9 +438,7 @@ "type": "string" } }, - "required": [ - "message" - ] + "required": ["message"] }, "CommentCreate": { "title": "CommentCreate", @@ -476,9 +448,7 @@ "commandType": { "title": "Commandtype", "default": "comment", - "enum": [ - "comment" - ], + "enum": ["comment"], "type": "string" }, "params": { @@ -498,9 +468,7 @@ "type": "string" } }, - "required": [ - "params" - ] + "required": ["params"] }, "ConfigureForVolumeParams": { "title": "ConfigureForVolumeParams", @@ -519,10 +487,7 @@ "type": "number" } }, - "required": [ - "pipetteId", - "volume" - ] + "required": ["pipetteId", "volume"] }, "ConfigureForVolumeCreate": { "title": "ConfigureForVolumeCreate", @@ -532,9 +497,7 @@ "commandType": { "title": "Commandtype", "default": "configureForVolume", - "enum": [ - "configureForVolume" - ], + "enum": ["configureForVolume"], "type": "string" }, "params": { @@ -554,9 +517,7 @@ "type": "string" } }, - "required": [ - "params" - ] + "required": ["params"] }, "AllNozzleLayoutConfiguration": { "title": "AllNozzleLayoutConfiguration", @@ -566,9 +527,7 @@ "style": { "title": "Style", "default": "ALL", - "enum": [ - "ALL" - ], + "enum": ["ALL"], "type": "string" } } @@ -581,26 +540,17 @@ "style": { "title": "Style", "default": "SINGLE", - "enum": [ - "SINGLE" - ], + "enum": ["SINGLE"], "type": "string" }, "primaryNozzle": { "title": "Primarynozzle", "description": "The primary nozzle to use in the layout configuration. This nozzle will update the critical point of the current pipette. For now, this is also the back left corner of your rectangle.", - "enum": [ - "A1", - "H1", - "A12", - "H12" - ], + "enum": ["A1", "H1", "A12", "H12"], "type": "string" } }, - "required": [ - "primaryNozzle" - ] + "required": ["primaryNozzle"] }, "RowNozzleLayoutConfiguration": { "title": "RowNozzleLayoutConfiguration", @@ -610,26 +560,17 @@ "style": { "title": "Style", "default": "ROW", - "enum": [ - "ROW" - ], + "enum": ["ROW"], "type": "string" }, "primaryNozzle": { "title": "Primarynozzle", "description": "The primary nozzle to use in the layout configuration. This nozzle will update the critical point of the current pipette. For now, this is also the back left corner of your rectangle.", - "enum": [ - "A1", - "H1", - "A12", - "H12" - ], + "enum": ["A1", "H1", "A12", "H12"], "type": "string" } }, - "required": [ - "primaryNozzle" - ] + "required": ["primaryNozzle"] }, "ColumnNozzleLayoutConfiguration": { "title": "ColumnNozzleLayoutConfiguration", @@ -639,26 +580,17 @@ "style": { "title": "Style", "default": "COLUMN", - "enum": [ - "COLUMN" - ], + "enum": ["COLUMN"], "type": "string" }, "primaryNozzle": { "title": "Primarynozzle", "description": "The primary nozzle to use in the layout configuration. This nozzle will update the critical point of the current pipette. For now, this is also the back left corner of your rectangle.", - "enum": [ - "A1", - "H1", - "A12", - "H12" - ], + "enum": ["A1", "H1", "A12", "H12"], "type": "string" } }, - "required": [ - "primaryNozzle" - ] + "required": ["primaryNozzle"] }, "QuadrantNozzleLayoutConfiguration": { "title": "QuadrantNozzleLayoutConfiguration", @@ -668,20 +600,13 @@ "style": { "title": "Style", "default": "QUADRANT", - "enum": [ - "QUADRANT" - ], + "enum": ["QUADRANT"], "type": "string" }, "primaryNozzle": { "title": "Primarynozzle", "description": "The primary nozzle to use in the layout configuration. This nozzle will update the critical point of the current pipette. For now, this is also the back left corner of your rectangle.", - "enum": [ - "A1", - "H1", - "A12", - "H12" - ], + "enum": ["A1", "H1", "A12", "H12"], "type": "string" }, "frontRightNozzle": { @@ -691,10 +616,7 @@ "type": "string" } }, - "required": [ - "primaryNozzle", - "frontRightNozzle" - ] + "required": ["primaryNozzle", "frontRightNozzle"] }, "ConfigureNozzleLayoutParams": { "title": "ConfigureNozzleLayoutParams", @@ -727,10 +649,7 @@ ] } }, - "required": [ - "pipetteId", - "configurationParams" - ] + "required": ["pipetteId", "configurationParams"] }, "ConfigureNozzleLayoutCreate": { "title": "ConfigureNozzleLayoutCreate", @@ -740,9 +659,7 @@ "commandType": { "title": "Commandtype", "default": "configureNozzleLayout", - "enum": [ - "configureNozzleLayout" - ], + "enum": ["configureNozzleLayout"], "type": "string" }, "params": { @@ -762,9 +679,7 @@ "type": "string" } }, - "required": [ - "params" - ] + "required": ["params"] }, "CustomParams": { "title": "CustomParams", @@ -780,9 +695,7 @@ "commandType": { "title": "Commandtype", "default": "custom", - "enum": [ - "custom" - ], + "enum": ["custom"], "type": "string" }, "params": { @@ -802,9 +715,7 @@ "type": "string" } }, - "required": [ - "params" - ] + "required": ["params"] }, "DispenseParams": { "title": "DispenseParams", @@ -853,13 +764,7 @@ "type": "number" } }, - "required": [ - "labwareId", - "wellName", - "flowRate", - "volume", - "pipetteId" - ] + "required": ["labwareId", "wellName", "flowRate", "volume", "pipetteId"] }, "DispenseCreate": { "title": "DispenseCreate", @@ -869,9 +774,7 @@ "commandType": { "title": "Commandtype", "default": "dispense", - "enum": [ - "dispense" - ], + "enum": ["dispense"], "type": "string" }, "params": { @@ -891,9 +794,7 @@ "type": "string" } }, - "required": [ - "params" - ] + "required": ["params"] }, "DispenseInPlaceParams": { "title": "DispenseInPlaceParams", @@ -923,11 +824,7 @@ "type": "number" } }, - "required": [ - "flowRate", - "volume", - "pipetteId" - ] + "required": ["flowRate", "volume", "pipetteId"] }, "DispenseInPlaceCreate": { "title": "DispenseInPlaceCreate", @@ -937,9 +834,7 @@ "commandType": { "title": "Commandtype", "default": "dispenseInPlace", - "enum": [ - "dispenseInPlace" - ], + "enum": ["dispenseInPlace"], "type": "string" }, "params": { @@ -959,9 +854,7 @@ "type": "string" } }, - "required": [ - "params" - ] + "required": ["params"] }, "BlowOutParams": { "title": "BlowOutParams", @@ -999,12 +892,7 @@ "type": "string" } }, - "required": [ - "labwareId", - "wellName", - "flowRate", - "pipetteId" - ] + "required": ["labwareId", "wellName", "flowRate", "pipetteId"] }, "BlowOutCreate": { "title": "BlowOutCreate", @@ -1014,9 +902,7 @@ "commandType": { "title": "Commandtype", "default": "blowout", - "enum": [ - "blowout" - ], + "enum": ["blowout"], "type": "string" }, "params": { @@ -1036,9 +922,7 @@ "type": "string" } }, - "required": [ - "params" - ] + "required": ["params"] }, "BlowOutInPlaceParams": { "title": "BlowOutInPlaceParams", @@ -1057,10 +941,7 @@ "type": "string" } }, - "required": [ - "flowRate", - "pipetteId" - ] + "required": ["flowRate", "pipetteId"] }, "BlowOutInPlaceCreate": { "title": "BlowOutInPlaceCreate", @@ -1070,9 +951,7 @@ "commandType": { "title": "Commandtype", "default": "blowOutInPlace", - "enum": [ - "blowOutInPlace" - ], + "enum": ["blowOutInPlace"], "type": "string" }, "params": { @@ -1092,19 +971,12 @@ "type": "string" } }, - "required": [ - "params" - ] + "required": ["params"] }, "DropTipWellOrigin": { "title": "DropTipWellOrigin", "description": "The origin of a DropTipWellLocation offset.\n\nProps:\n TOP: the top-center of the well\n BOTTOM: the bottom-center of the well\n CENTER: the middle-center of the well\n DEFAULT: the default drop-tip location of the well,\n based on pipette configuration and length of the tip.", - "enum": [ - "top", - "bottom", - "center", - "default" - ], + "enum": ["top", "bottom", "center", "default"], "type": "string" }, "DropTipWellLocation": { @@ -1166,11 +1038,7 @@ "type": "boolean" } }, - "required": [ - "pipetteId", - "labwareId", - "wellName" - ] + "required": ["pipetteId", "labwareId", "wellName"] }, "DropTipCreate": { "title": "DropTipCreate", @@ -1180,9 +1048,7 @@ "commandType": { "title": "Commandtype", "default": "dropTip", - "enum": [ - "dropTip" - ], + "enum": ["dropTip"], "type": "string" }, "params": { @@ -1202,9 +1068,7 @@ "type": "string" } }, - "required": [ - "params" - ] + "required": ["params"] }, "DropTipInPlaceParams": { "title": "DropTipInPlaceParams", @@ -1222,9 +1086,7 @@ "type": "boolean" } }, - "required": [ - "pipetteId" - ] + "required": ["pipetteId"] }, "DropTipInPlaceCreate": { "title": "DropTipInPlaceCreate", @@ -1234,9 +1096,7 @@ "commandType": { "title": "Commandtype", "default": "dropTipInPlace", - "enum": [ - "dropTipInPlace" - ], + "enum": ["dropTipInPlace"], "type": "string" }, "params": { @@ -1256,9 +1116,7 @@ "type": "string" } }, - "required": [ - "params" - ] + "required": ["params"] }, "MotorAxis": { "title": "MotorAxis", @@ -1278,11 +1136,7 @@ "MountType": { "title": "MountType", "description": "An enumeration.", - "enum": [ - "left", - "right", - "extension" - ], + "enum": ["left", "right", "extension"], "type": "string" }, "HomeParams": { @@ -1315,9 +1169,7 @@ "commandType": { "title": "Commandtype", "default": "home", - "enum": [ - "home" - ], + "enum": ["home"], "type": "string" }, "params": { @@ -1337,9 +1189,7 @@ "type": "string" } }, - "required": [ - "params" - ] + "required": ["params"] }, "RetractAxisParams": { "title": "RetractAxisParams", @@ -1355,9 +1205,7 @@ ] } }, - "required": [ - "axis" - ] + "required": ["axis"] }, "RetractAxisCreate": { "title": "RetractAxisCreate", @@ -1367,9 +1215,7 @@ "commandType": { "title": "Commandtype", "default": "retractAxis", - "enum": [ - "retractAxis" - ], + "enum": ["retractAxis"], "type": "string" }, "params": { @@ -1389,9 +1235,7 @@ "type": "string" } }, - "required": [ - "params" - ] + "required": ["params"] }, "DeckSlotName": { "title": "DeckSlotName", @@ -1437,9 +1281,7 @@ ] } }, - "required": [ - "slotName" - ] + "required": ["slotName"] }, "ModuleLocation": { "title": "ModuleLocation", @@ -1452,9 +1294,7 @@ "type": "string" } }, - "required": [ - "moduleId" - ] + "required": ["moduleId"] }, "OnLabwareLocation": { "title": "OnLabwareLocation", @@ -1467,9 +1307,7 @@ "type": "string" } }, - "required": [ - "labwareId" - ] + "required": ["labwareId"] }, "AddressableAreaLocation": { "title": "AddressableAreaLocation", @@ -1482,9 +1320,7 @@ "type": "string" } }, - "required": [ - "addressableAreaName" - ] + "required": ["addressableAreaName"] }, "LoadLabwareParams": { "title": "LoadLabwareParams", @@ -1505,9 +1341,7 @@ "$ref": "#/definitions/OnLabwareLocation" }, { - "enum": [ - "offDeck" - ], + "enum": ["offDeck"], "type": "string" }, { @@ -1541,12 +1375,7 @@ "type": "string" } }, - "required": [ - "location", - "loadName", - "namespace", - "version" - ] + "required": ["location", "loadName", "namespace", "version"] }, "LoadLabwareCreate": { "title": "LoadLabwareCreate", @@ -1556,9 +1385,7 @@ "commandType": { "title": "Commandtype", "default": "loadLabware", - "enum": [ - "loadLabware" - ], + "enum": ["loadLabware"], "type": "string" }, "params": { @@ -1578,9 +1405,7 @@ "type": "string" } }, - "required": [ - "params" - ] + "required": ["params"] }, "LoadLiquidParams": { "title": "LoadLiquidParams", @@ -1606,11 +1431,7 @@ } } }, - "required": [ - "liquidId", - "labwareId", - "volumeByWell" - ] + "required": ["liquidId", "labwareId", "volumeByWell"] }, "LoadLiquidCreate": { "title": "LoadLiquidCreate", @@ -1620,9 +1441,7 @@ "commandType": { "title": "Commandtype", "default": "loadLiquid", - "enum": [ - "loadLiquid" - ], + "enum": ["loadLiquid"], "type": "string" }, "params": { @@ -1642,9 +1461,7 @@ "type": "string" } }, - "required": [ - "params" - ] + "required": ["params"] }, "ModuleModel": { "title": "ModuleModel", @@ -1689,10 +1506,7 @@ "type": "string" } }, - "required": [ - "model", - "location" - ] + "required": ["model", "location"] }, "LoadModuleCreate": { "title": "LoadModuleCreate", @@ -1702,9 +1516,7 @@ "commandType": { "title": "Commandtype", "default": "loadModule", - "enum": [ - "loadModule" - ], + "enum": ["loadModule"], "type": "string" }, "params": { @@ -1724,9 +1536,7 @@ "type": "string" } }, - "required": [ - "params" - ] + "required": ["params"] }, "PipetteNameType": { "title": "PipetteNameType", @@ -1779,10 +1589,7 @@ "type": "string" } }, - "required": [ - "pipetteName", - "mount" - ] + "required": ["pipetteName", "mount"] }, "LoadPipetteCreate": { "title": "LoadPipetteCreate", @@ -1792,9 +1599,7 @@ "commandType": { "title": "Commandtype", "default": "loadPipette", - "enum": [ - "loadPipette" - ], + "enum": ["loadPipette"], "type": "string" }, "params": { @@ -1814,18 +1619,12 @@ "type": "string" } }, - "required": [ - "params" - ] + "required": ["params"] }, "LabwareMovementStrategy": { "title": "LabwareMovementStrategy", "description": "Strategy to use for labware movement.", - "enum": [ - "usingGripper", - "manualMoveWithPause", - "manualMoveWithoutPause" - ], + "enum": ["usingGripper", "manualMoveWithPause", "manualMoveWithoutPause"], "type": "string" }, "LabwareOffsetVector": { @@ -1846,11 +1645,7 @@ "type": "number" } }, - "required": [ - "x", - "y", - "z" - ] + "required": ["x", "y", "z"] }, "MoveLabwareParams": { "title": "MoveLabwareParams", @@ -1876,9 +1671,7 @@ "$ref": "#/definitions/OnLabwareLocation" }, { - "enum": [ - "offDeck" - ], + "enum": ["offDeck"], "type": "string" }, { @@ -1913,11 +1706,7 @@ ] } }, - "required": [ - "labwareId", - "newLocation", - "strategy" - ] + "required": ["labwareId", "newLocation", "strategy"] }, "MoveLabwareCreate": { "title": "MoveLabwareCreate", @@ -1927,9 +1716,7 @@ "commandType": { "title": "Commandtype", "default": "moveLabware", - "enum": [ - "moveLabware" - ], + "enum": ["moveLabware"], "type": "string" }, "params": { @@ -1949,18 +1736,12 @@ "type": "string" } }, - "required": [ - "params" - ] + "required": ["params"] }, "MovementAxis": { "title": "MovementAxis", "description": "Axis on which to issue a relative movement.", - "enum": [ - "x", - "y", - "z" - ], + "enum": ["x", "y", "z"], "type": "string" }, "MoveRelativeParams": { @@ -1987,11 +1768,7 @@ "type": "number" } }, - "required": [ - "pipetteId", - "axis", - "distance" - ] + "required": ["pipetteId", "axis", "distance"] }, "MoveRelativeCreate": { "title": "MoveRelativeCreate", @@ -2001,9 +1778,7 @@ "commandType": { "title": "Commandtype", "default": "moveRelative", - "enum": [ - "moveRelative" - ], + "enum": ["moveRelative"], "type": "string" }, "params": { @@ -2023,9 +1798,7 @@ "type": "string" } }, - "required": [ - "params" - ] + "required": ["params"] }, "DeckPoint": { "title": "DeckPoint", @@ -2045,11 +1818,7 @@ "type": "number" } }, - "required": [ - "x", - "y", - "z" - ] + "required": ["x", "y", "z"] }, "MoveToCoordinatesParams": { "title": "MoveToCoordinatesParams", @@ -2087,10 +1856,7 @@ ] } }, - "required": [ - "pipetteId", - "coordinates" - ] + "required": ["pipetteId", "coordinates"] }, "MoveToCoordinatesCreate": { "title": "MoveToCoordinatesCreate", @@ -2100,9 +1866,7 @@ "commandType": { "title": "Commandtype", "default": "moveToCoordinates", - "enum": [ - "moveToCoordinates" - ], + "enum": ["moveToCoordinates"], "type": "string" }, "params": { @@ -2122,9 +1886,7 @@ "type": "string" } }, - "required": [ - "params" - ] + "required": ["params"] }, "MoveToWellParams": { "title": "MoveToWellParams", @@ -2172,11 +1934,7 @@ "type": "string" } }, - "required": [ - "labwareId", - "wellName", - "pipetteId" - ] + "required": ["labwareId", "wellName", "pipetteId"] }, "MoveToWellCreate": { "title": "MoveToWellCreate", @@ -2186,9 +1944,7 @@ "commandType": { "title": "Commandtype", "default": "moveToWell", - "enum": [ - "moveToWell" - ], + "enum": ["moveToWell"], "type": "string" }, "params": { @@ -2208,9 +1964,7 @@ "type": "string" } }, - "required": [ - "params" - ] + "required": ["params"] }, "AddressableOffsetVector": { "title": "AddressableOffsetVector", @@ -2230,11 +1984,7 @@ "type": "number" } }, - "required": [ - "x", - "y", - "z" - ] + "required": ["x", "y", "z"] }, "MoveToAddressableAreaParams": { "title": "MoveToAddressableAreaParams", @@ -2288,10 +2038,7 @@ "type": "boolean" } }, - "required": [ - "pipetteId", - "addressableAreaName" - ] + "required": ["pipetteId", "addressableAreaName"] }, "MoveToAddressableAreaCreate": { "title": "MoveToAddressableAreaCreate", @@ -2301,9 +2048,7 @@ "commandType": { "title": "Commandtype", "default": "moveToAddressableArea", - "enum": [ - "moveToAddressableArea" - ], + "enum": ["moveToAddressableArea"], "type": "string" }, "params": { @@ -2323,9 +2068,7 @@ "type": "string" } }, - "required": [ - "params" - ] + "required": ["params"] }, "MoveToAddressableAreaForDropTipParams": { "title": "MoveToAddressableAreaForDropTipParams", @@ -2385,10 +2128,7 @@ "type": "boolean" } }, - "required": [ - "pipetteId", - "addressableAreaName" - ] + "required": ["pipetteId", "addressableAreaName"] }, "MoveToAddressableAreaForDropTipCreate": { "title": "MoveToAddressableAreaForDropTipCreate", @@ -2398,9 +2138,7 @@ "commandType": { "title": "Commandtype", "default": "moveToAddressableAreaForDropTip", - "enum": [ - "moveToAddressableAreaForDropTip" - ], + "enum": ["moveToAddressableAreaForDropTip"], "type": "string" }, "params": { @@ -2420,9 +2158,7 @@ "type": "string" } }, - "required": [ - "params" - ] + "required": ["params"] }, "PrepareToAspirateParams": { "title": "PrepareToAspirateParams", @@ -2435,9 +2171,7 @@ "type": "string" } }, - "required": [ - "pipetteId" - ] + "required": ["pipetteId"] }, "PrepareToAspirateCreate": { "title": "PrepareToAspirateCreate", @@ -2447,9 +2181,7 @@ "commandType": { "title": "Commandtype", "default": "prepareToAspirate", - "enum": [ - "prepareToAspirate" - ], + "enum": ["prepareToAspirate"], "type": "string" }, "params": { @@ -2469,9 +2201,7 @@ "type": "string" } }, - "required": [ - "params" - ] + "required": ["params"] }, "WaitForResumeParams": { "title": "WaitForResumeParams", @@ -2493,10 +2223,7 @@ "commandType": { "title": "Commandtype", "default": "waitForResume", - "enum": [ - "waitForResume", - "pause" - ], + "enum": ["waitForResume", "pause"], "type": "string" }, "params": { @@ -2516,9 +2243,7 @@ "type": "string" } }, - "required": [ - "params" - ] + "required": ["params"] }, "WaitForDurationParams": { "title": "WaitForDurationParams", @@ -2536,9 +2261,7 @@ "type": "string" } }, - "required": [ - "seconds" - ] + "required": ["seconds"] }, "WaitForDurationCreate": { "title": "WaitForDurationCreate", @@ -2548,9 +2271,7 @@ "commandType": { "title": "Commandtype", "default": "waitForDuration", - "enum": [ - "waitForDuration" - ], + "enum": ["waitForDuration"], "type": "string" }, "params": { @@ -2570,9 +2291,7 @@ "type": "string" } }, - "required": [ - "params" - ] + "required": ["params"] }, "PickUpTipParams": { "title": "PickUpTipParams", @@ -2604,11 +2323,7 @@ "type": "string" } }, - "required": [ - "labwareId", - "wellName", - "pipetteId" - ] + "required": ["labwareId", "wellName", "pipetteId"] }, "PickUpTipCreate": { "title": "PickUpTipCreate", @@ -2618,9 +2333,7 @@ "commandType": { "title": "Commandtype", "default": "pickUpTip", - "enum": [ - "pickUpTip" - ], + "enum": ["pickUpTip"], "type": "string" }, "params": { @@ -2640,9 +2353,7 @@ "type": "string" } }, - "required": [ - "params" - ] + "required": ["params"] }, "SavePositionParams": { "title": "SavePositionParams", @@ -2666,9 +2377,7 @@ "type": "boolean" } }, - "required": [ - "pipetteId" - ] + "required": ["pipetteId"] }, "SavePositionCreate": { "title": "SavePositionCreate", @@ -2678,9 +2387,7 @@ "commandType": { "title": "Commandtype", "default": "savePosition", - "enum": [ - "savePosition" - ], + "enum": ["savePosition"], "type": "string" }, "params": { @@ -2700,9 +2407,7 @@ "type": "string" } }, - "required": [ - "params" - ] + "required": ["params"] }, "SetRailLightsParams": { "title": "SetRailLightsParams", @@ -2715,9 +2420,7 @@ "type": "boolean" } }, - "required": [ - "on" - ] + "required": ["on"] }, "SetRailLightsCreate": { "title": "SetRailLightsCreate", @@ -2727,9 +2430,7 @@ "commandType": { "title": "Commandtype", "default": "setRailLights", - "enum": [ - "setRailLights" - ], + "enum": ["setRailLights"], "type": "string" }, "params": { @@ -2749,9 +2450,7 @@ "type": "string" } }, - "required": [ - "params" - ] + "required": ["params"] }, "TouchTipParams": { "title": "TouchTipParams", @@ -2794,11 +2493,7 @@ "type": "number" } }, - "required": [ - "labwareId", - "wellName", - "pipetteId" - ] + "required": ["labwareId", "wellName", "pipetteId"] }, "TouchTipCreate": { "title": "TouchTipCreate", @@ -2808,9 +2503,7 @@ "commandType": { "title": "Commandtype", "default": "touchTip", - "enum": [ - "touchTip" - ], + "enum": ["touchTip"], "type": "string" }, "params": { @@ -2830,20 +2523,12 @@ "type": "string" } }, - "required": [ - "params" - ] + "required": ["params"] }, "StatusBarAnimation": { "title": "StatusBarAnimation", "description": "Status Bar animation options.", - "enum": [ - "idle", - "confirm", - "updating", - "disco", - "off" - ] + "enum": ["idle", "confirm", "updating", "disco", "off"] }, "SetStatusBarParams": { "title": "SetStatusBarParams", @@ -2859,9 +2544,7 @@ ] } }, - "required": [ - "animation" - ] + "required": ["animation"] }, "SetStatusBarCreate": { "title": "SetStatusBarCreate", @@ -2871,9 +2554,7 @@ "commandType": { "title": "Commandtype", "default": "setStatusBar", - "enum": [ - "setStatusBar" - ], + "enum": ["setStatusBar"], "type": "string" }, "params": { @@ -2893,18 +2574,12 @@ "type": "string" } }, - "required": [ - "params" - ] + "required": ["params"] }, "TipPresenceStatus": { "title": "TipPresenceStatus", "description": "Tip presence status reported by a pipette.", - "enum": [ - "present", - "absent", - "unknown" - ], + "enum": ["present", "absent", "unknown"], "type": "string" }, "VerifyTipPresenceParams": { @@ -2926,10 +2601,7 @@ ] } }, - "required": [ - "pipetteId", - "expectedState" - ] + "required": ["pipetteId", "expectedState"] }, "VerifyTipPresenceCreate": { "title": "VerifyTipPresenceCreate", @@ -2939,9 +2611,7 @@ "commandType": { "title": "Commandtype", "default": "verifyTipPresence", - "enum": [ - "verifyTipPresence" - ], + "enum": ["verifyTipPresence"], "type": "string" }, "params": { @@ -2961,9 +2631,7 @@ "type": "string" } }, - "required": [ - "params" - ] + "required": ["params"] }, "GetTipPresenceParams": { "title": "GetTipPresenceParams", @@ -2976,9 +2644,7 @@ "type": "string" } }, - "required": [ - "pipetteId" - ] + "required": ["pipetteId"] }, "GetTipPresenceCreate": { "title": "GetTipPresenceCreate", @@ -2988,9 +2654,7 @@ "commandType": { "title": "Commandtype", "default": "getTipPresence", - "enum": [ - "getTipPresence" - ], + "enum": ["getTipPresence"], "type": "string" }, "params": { @@ -3010,9 +2674,7 @@ "type": "string" } }, - "required": [ - "params" - ] + "required": ["params"] }, "opentrons__protocol_engine__commands__heater_shaker__wait_for_temperature__WaitForTemperatureParams": { "title": "WaitForTemperatureParams", @@ -3030,9 +2692,7 @@ "type": "number" } }, - "required": [ - "moduleId" - ] + "required": ["moduleId"] }, "opentrons__protocol_engine__commands__heater_shaker__wait_for_temperature__WaitForTemperatureCreate": { "title": "WaitForTemperatureCreate", @@ -3042,9 +2702,7 @@ "commandType": { "title": "Commandtype", "default": "heaterShaker/waitForTemperature", - "enum": [ - "heaterShaker/waitForTemperature" - ], + "enum": ["heaterShaker/waitForTemperature"], "type": "string" }, "params": { @@ -3064,9 +2722,7 @@ "type": "string" } }, - "required": [ - "params" - ] + "required": ["params"] }, "opentrons__protocol_engine__commands__heater_shaker__set_target_temperature__SetTargetTemperatureParams": { "title": "SetTargetTemperatureParams", @@ -3084,10 +2740,7 @@ "type": "number" } }, - "required": [ - "moduleId", - "celsius" - ] + "required": ["moduleId", "celsius"] }, "opentrons__protocol_engine__commands__heater_shaker__set_target_temperature__SetTargetTemperatureCreate": { "title": "SetTargetTemperatureCreate", @@ -3097,9 +2750,7 @@ "commandType": { "title": "Commandtype", "default": "heaterShaker/setTargetTemperature", - "enum": [ - "heaterShaker/setTargetTemperature" - ], + "enum": ["heaterShaker/setTargetTemperature"], "type": "string" }, "params": { @@ -3119,9 +2770,7 @@ "type": "string" } }, - "required": [ - "params" - ] + "required": ["params"] }, "DeactivateHeaterParams": { "title": "DeactivateHeaterParams", @@ -3134,9 +2783,7 @@ "type": "string" } }, - "required": [ - "moduleId" - ] + "required": ["moduleId"] }, "DeactivateHeaterCreate": { "title": "DeactivateHeaterCreate", @@ -3146,9 +2793,7 @@ "commandType": { "title": "Commandtype", "default": "heaterShaker/deactivateHeater", - "enum": [ - "heaterShaker/deactivateHeater" - ], + "enum": ["heaterShaker/deactivateHeater"], "type": "string" }, "params": { @@ -3168,9 +2813,7 @@ "type": "string" } }, - "required": [ - "params" - ] + "required": ["params"] }, "SetAndWaitForShakeSpeedParams": { "title": "SetAndWaitForShakeSpeedParams", @@ -3188,10 +2831,7 @@ "type": "number" } }, - "required": [ - "moduleId", - "rpm" - ] + "required": ["moduleId", "rpm"] }, "SetAndWaitForShakeSpeedCreate": { "title": "SetAndWaitForShakeSpeedCreate", @@ -3201,9 +2841,7 @@ "commandType": { "title": "Commandtype", "default": "heaterShaker/setAndWaitForShakeSpeed", - "enum": [ - "heaterShaker/setAndWaitForShakeSpeed" - ], + "enum": ["heaterShaker/setAndWaitForShakeSpeed"], "type": "string" }, "params": { @@ -3223,9 +2861,7 @@ "type": "string" } }, - "required": [ - "params" - ] + "required": ["params"] }, "DeactivateShakerParams": { "title": "DeactivateShakerParams", @@ -3238,9 +2874,7 @@ "type": "string" } }, - "required": [ - "moduleId" - ] + "required": ["moduleId"] }, "DeactivateShakerCreate": { "title": "DeactivateShakerCreate", @@ -3250,9 +2884,7 @@ "commandType": { "title": "Commandtype", "default": "heaterShaker/deactivateShaker", - "enum": [ - "heaterShaker/deactivateShaker" - ], + "enum": ["heaterShaker/deactivateShaker"], "type": "string" }, "params": { @@ -3272,9 +2904,7 @@ "type": "string" } }, - "required": [ - "params" - ] + "required": ["params"] }, "OpenLabwareLatchParams": { "title": "OpenLabwareLatchParams", @@ -3287,9 +2917,7 @@ "type": "string" } }, - "required": [ - "moduleId" - ] + "required": ["moduleId"] }, "OpenLabwareLatchCreate": { "title": "OpenLabwareLatchCreate", @@ -3299,9 +2927,7 @@ "commandType": { "title": "Commandtype", "default": "heaterShaker/openLabwareLatch", - "enum": [ - "heaterShaker/openLabwareLatch" - ], + "enum": ["heaterShaker/openLabwareLatch"], "type": "string" }, "params": { @@ -3321,9 +2947,7 @@ "type": "string" } }, - "required": [ - "params" - ] + "required": ["params"] }, "CloseLabwareLatchParams": { "title": "CloseLabwareLatchParams", @@ -3336,9 +2960,7 @@ "type": "string" } }, - "required": [ - "moduleId" - ] + "required": ["moduleId"] }, "CloseLabwareLatchCreate": { "title": "CloseLabwareLatchCreate", @@ -3348,9 +2970,7 @@ "commandType": { "title": "Commandtype", "default": "heaterShaker/closeLabwareLatch", - "enum": [ - "heaterShaker/closeLabwareLatch" - ], + "enum": ["heaterShaker/closeLabwareLatch"], "type": "string" }, "params": { @@ -3370,9 +2990,7 @@ "type": "string" } }, - "required": [ - "params" - ] + "required": ["params"] }, "DisengageParams": { "title": "DisengageParams", @@ -3385,9 +3003,7 @@ "type": "string" } }, - "required": [ - "moduleId" - ] + "required": ["moduleId"] }, "DisengageCreate": { "title": "DisengageCreate", @@ -3397,9 +3013,7 @@ "commandType": { "title": "Commandtype", "default": "magneticModule/disengage", - "enum": [ - "magneticModule/disengage" - ], + "enum": ["magneticModule/disengage"], "type": "string" }, "params": { @@ -3419,9 +3033,7 @@ "type": "string" } }, - "required": [ - "params" - ] + "required": ["params"] }, "EngageParams": { "title": "EngageParams", @@ -3439,10 +3051,7 @@ "type": "number" } }, - "required": [ - "moduleId", - "height" - ] + "required": ["moduleId", "height"] }, "EngageCreate": { "title": "EngageCreate", @@ -3452,9 +3061,7 @@ "commandType": { "title": "Commandtype", "default": "magneticModule/engage", - "enum": [ - "magneticModule/engage" - ], + "enum": ["magneticModule/engage"], "type": "string" }, "params": { @@ -3474,9 +3081,7 @@ "type": "string" } }, - "required": [ - "params" - ] + "required": ["params"] }, "opentrons__protocol_engine__commands__temperature_module__set_target_temperature__SetTargetTemperatureParams": { "title": "SetTargetTemperatureParams", @@ -3494,10 +3099,7 @@ "type": "number" } }, - "required": [ - "moduleId", - "celsius" - ] + "required": ["moduleId", "celsius"] }, "opentrons__protocol_engine__commands__temperature_module__set_target_temperature__SetTargetTemperatureCreate": { "title": "SetTargetTemperatureCreate", @@ -3507,9 +3109,7 @@ "commandType": { "title": "Commandtype", "default": "temperatureModule/setTargetTemperature", - "enum": [ - "temperatureModule/setTargetTemperature" - ], + "enum": ["temperatureModule/setTargetTemperature"], "type": "string" }, "params": { @@ -3529,9 +3129,7 @@ "type": "string" } }, - "required": [ - "params" - ] + "required": ["params"] }, "opentrons__protocol_engine__commands__temperature_module__wait_for_temperature__WaitForTemperatureParams": { "title": "WaitForTemperatureParams", @@ -3549,9 +3147,7 @@ "type": "number" } }, - "required": [ - "moduleId" - ] + "required": ["moduleId"] }, "opentrons__protocol_engine__commands__temperature_module__wait_for_temperature__WaitForTemperatureCreate": { "title": "WaitForTemperatureCreate", @@ -3561,9 +3157,7 @@ "commandType": { "title": "Commandtype", "default": "temperatureModule/waitForTemperature", - "enum": [ - "temperatureModule/waitForTemperature" - ], + "enum": ["temperatureModule/waitForTemperature"], "type": "string" }, "params": { @@ -3583,9 +3177,7 @@ "type": "string" } }, - "required": [ - "params" - ] + "required": ["params"] }, "DeactivateTemperatureParams": { "title": "DeactivateTemperatureParams", @@ -3598,9 +3190,7 @@ "type": "string" } }, - "required": [ - "moduleId" - ] + "required": ["moduleId"] }, "DeactivateTemperatureCreate": { "title": "DeactivateTemperatureCreate", @@ -3610,9 +3200,7 @@ "commandType": { "title": "Commandtype", "default": "temperatureModule/deactivate", - "enum": [ - "temperatureModule/deactivate" - ], + "enum": ["temperatureModule/deactivate"], "type": "string" }, "params": { @@ -3632,9 +3220,7 @@ "type": "string" } }, - "required": [ - "params" - ] + "required": ["params"] }, "SetTargetBlockTemperatureParams": { "title": "SetTargetBlockTemperatureParams", @@ -3662,10 +3248,7 @@ "type": "number" } }, - "required": [ - "moduleId", - "celsius" - ] + "required": ["moduleId", "celsius"] }, "SetTargetBlockTemperatureCreate": { "title": "SetTargetBlockTemperatureCreate", @@ -3675,9 +3258,7 @@ "commandType": { "title": "Commandtype", "default": "thermocycler/setTargetBlockTemperature", - "enum": [ - "thermocycler/setTargetBlockTemperature" - ], + "enum": ["thermocycler/setTargetBlockTemperature"], "type": "string" }, "params": { @@ -3697,9 +3278,7 @@ "type": "string" } }, - "required": [ - "params" - ] + "required": ["params"] }, "WaitForBlockTemperatureParams": { "title": "WaitForBlockTemperatureParams", @@ -3712,9 +3291,7 @@ "type": "string" } }, - "required": [ - "moduleId" - ] + "required": ["moduleId"] }, "WaitForBlockTemperatureCreate": { "title": "WaitForBlockTemperatureCreate", @@ -3724,9 +3301,7 @@ "commandType": { "title": "Commandtype", "default": "thermocycler/waitForBlockTemperature", - "enum": [ - "thermocycler/waitForBlockTemperature" - ], + "enum": ["thermocycler/waitForBlockTemperature"], "type": "string" }, "params": { @@ -3746,9 +3321,7 @@ "type": "string" } }, - "required": [ - "params" - ] + "required": ["params"] }, "SetTargetLidTemperatureParams": { "title": "SetTargetLidTemperatureParams", @@ -3766,10 +3339,7 @@ "type": "number" } }, - "required": [ - "moduleId", - "celsius" - ] + "required": ["moduleId", "celsius"] }, "SetTargetLidTemperatureCreate": { "title": "SetTargetLidTemperatureCreate", @@ -3779,9 +3349,7 @@ "commandType": { "title": "Commandtype", "default": "thermocycler/setTargetLidTemperature", - "enum": [ - "thermocycler/setTargetLidTemperature" - ], + "enum": ["thermocycler/setTargetLidTemperature"], "type": "string" }, "params": { @@ -3801,9 +3369,7 @@ "type": "string" } }, - "required": [ - "params" - ] + "required": ["params"] }, "WaitForLidTemperatureParams": { "title": "WaitForLidTemperatureParams", @@ -3816,9 +3382,7 @@ "type": "string" } }, - "required": [ - "moduleId" - ] + "required": ["moduleId"] }, "WaitForLidTemperatureCreate": { "title": "WaitForLidTemperatureCreate", @@ -3828,9 +3392,7 @@ "commandType": { "title": "Commandtype", "default": "thermocycler/waitForLidTemperature", - "enum": [ - "thermocycler/waitForLidTemperature" - ], + "enum": ["thermocycler/waitForLidTemperature"], "type": "string" }, "params": { @@ -3850,9 +3412,7 @@ "type": "string" } }, - "required": [ - "params" - ] + "required": ["params"] }, "DeactivateBlockParams": { "title": "DeactivateBlockParams", @@ -3865,9 +3425,7 @@ "type": "string" } }, - "required": [ - "moduleId" - ] + "required": ["moduleId"] }, "DeactivateBlockCreate": { "title": "DeactivateBlockCreate", @@ -3877,9 +3435,7 @@ "commandType": { "title": "Commandtype", "default": "thermocycler/deactivateBlock", - "enum": [ - "thermocycler/deactivateBlock" - ], + "enum": ["thermocycler/deactivateBlock"], "type": "string" }, "params": { @@ -3899,9 +3455,7 @@ "type": "string" } }, - "required": [ - "params" - ] + "required": ["params"] }, "DeactivateLidParams": { "title": "DeactivateLidParams", @@ -3914,9 +3468,7 @@ "type": "string" } }, - "required": [ - "moduleId" - ] + "required": ["moduleId"] }, "DeactivateLidCreate": { "title": "DeactivateLidCreate", @@ -3926,9 +3478,7 @@ "commandType": { "title": "Commandtype", "default": "thermocycler/deactivateLid", - "enum": [ - "thermocycler/deactivateLid" - ], + "enum": ["thermocycler/deactivateLid"], "type": "string" }, "params": { @@ -3948,9 +3498,7 @@ "type": "string" } }, - "required": [ - "params" - ] + "required": ["params"] }, "OpenLidParams": { "title": "OpenLidParams", @@ -3963,9 +3511,7 @@ "type": "string" } }, - "required": [ - "moduleId" - ] + "required": ["moduleId"] }, "OpenLidCreate": { "title": "OpenLidCreate", @@ -3975,9 +3521,7 @@ "commandType": { "title": "Commandtype", "default": "thermocycler/openLid", - "enum": [ - "thermocycler/openLid" - ], + "enum": ["thermocycler/openLid"], "type": "string" }, "params": { @@ -3997,9 +3541,7 @@ "type": "string" } }, - "required": [ - "params" - ] + "required": ["params"] }, "CloseLidParams": { "title": "CloseLidParams", @@ -4012,9 +3554,7 @@ "type": "string" } }, - "required": [ - "moduleId" - ] + "required": ["moduleId"] }, "CloseLidCreate": { "title": "CloseLidCreate", @@ -4024,9 +3564,7 @@ "commandType": { "title": "Commandtype", "default": "thermocycler/closeLid", - "enum": [ - "thermocycler/closeLid" - ], + "enum": ["thermocycler/closeLid"], "type": "string" }, "params": { @@ -4046,9 +3584,7 @@ "type": "string" } }, - "required": [ - "params" - ] + "required": ["params"] }, "RunProfileStepParams": { "title": "RunProfileStepParams", @@ -4066,10 +3602,7 @@ "type": "number" } }, - "required": [ - "celsius", - "holdSeconds" - ] + "required": ["celsius", "holdSeconds"] }, "RunProfileParams": { "title": "RunProfileParams", @@ -4095,10 +3628,7 @@ "type": "number" } }, - "required": [ - "moduleId", - "profile" - ] + "required": ["moduleId", "profile"] }, "RunProfileCreate": { "title": "RunProfileCreate", @@ -4108,9 +3638,7 @@ "commandType": { "title": "Commandtype", "default": "thermocycler/runProfile", - "enum": [ - "thermocycler/runProfile" - ], + "enum": ["thermocycler/runProfile"], "type": "string" }, "params": { @@ -4130,17 +3658,12 @@ "type": "string" } }, - "required": [ - "params" - ] + "required": ["params"] }, "CalibrateGripperParamsJaw": { "title": "CalibrateGripperParamsJaw", "description": "An enumeration.", - "enum": [ - "front", - "rear" - ] + "enum": ["front", "rear"] }, "Vec3f": { "title": "Vec3f", @@ -4160,11 +3683,7 @@ "type": "number" } }, - "required": [ - "x", - "y", - "z" - ] + "required": ["x", "y", "z"] }, "CalibrateGripperParams": { "title": "CalibrateGripperParams", @@ -4189,9 +3708,7 @@ ] } }, - "required": [ - "jaw" - ] + "required": ["jaw"] }, "CalibrateGripperCreate": { "title": "CalibrateGripperCreate", @@ -4201,9 +3718,7 @@ "commandType": { "title": "Commandtype", "default": "calibration/calibrateGripper", - "enum": [ - "calibration/calibrateGripper" - ], + "enum": ["calibration/calibrateGripper"], "type": "string" }, "params": { @@ -4223,9 +3738,7 @@ "type": "string" } }, - "required": [ - "params" - ] + "required": ["params"] }, "CalibratePipetteParams": { "title": "CalibratePipetteParams", @@ -4241,9 +3754,7 @@ ] } }, - "required": [ - "mount" - ] + "required": ["mount"] }, "CalibratePipetteCreate": { "title": "CalibratePipetteCreate", @@ -4253,9 +3764,7 @@ "commandType": { "title": "Commandtype", "default": "calibration/calibratePipette", - "enum": [ - "calibration/calibratePipette" - ], + "enum": ["calibration/calibratePipette"], "type": "string" }, "params": { @@ -4275,9 +3784,7 @@ "type": "string" } }, - "required": [ - "params" - ] + "required": ["params"] }, "CalibrateModuleParams": { "title": "CalibrateModuleParams", @@ -4303,11 +3810,7 @@ ] } }, - "required": [ - "moduleId", - "labwareId", - "mount" - ] + "required": ["moduleId", "labwareId", "mount"] }, "CalibrateModuleCreate": { "title": "CalibrateModuleCreate", @@ -4317,9 +3820,7 @@ "commandType": { "title": "Commandtype", "default": "calibration/calibrateModule", - "enum": [ - "calibration/calibrateModule" - ], + "enum": ["calibration/calibrateModule"], "type": "string" }, "params": { @@ -4339,17 +3840,12 @@ "type": "string" } }, - "required": [ - "params" - ] + "required": ["params"] }, "MaintenancePosition": { "title": "MaintenancePosition", "description": "Maintenance position options.", - "enum": [ - "attachPlate", - "attachInstrument" - ] + "enum": ["attachPlate", "attachInstrument"] }, "MoveToMaintenancePositionParams": { "title": "MoveToMaintenancePositionParams", @@ -4374,9 +3870,7 @@ ] } }, - "required": [ - "mount" - ] + "required": ["mount"] }, "MoveToMaintenancePositionCreate": { "title": "MoveToMaintenancePositionCreate", @@ -4386,9 +3880,7 @@ "commandType": { "title": "Commandtype", "default": "calibration/moveToMaintenancePosition", - "enum": [ - "calibration/moveToMaintenancePosition" - ], + "enum": ["calibration/moveToMaintenancePosition"], "type": "string" }, "params": { @@ -4408,9 +3900,7 @@ "type": "string" } }, - "required": [ - "params" - ] + "required": ["params"] } }, "$id": "opentronsCommandSchemaV8", From 775bb8af003ce28d61348b3de41809d5225f3798 Mon Sep 17 00:00:00 2001 From: tamarzanzouri Date: Mon, 22 Apr 2024 15:19:24 -0400 Subject: [PATCH 41/41] removed tavern test for fixit --- ...json_v6_run_failure_with_fixit.tavern.yaml | 124 ------------------ 1 file changed, 124 deletions(-) delete mode 100644 robot-server/tests/integration/http_api/runs/test_json_v6_run_failure_with_fixit.tavern.yaml diff --git a/robot-server/tests/integration/http_api/runs/test_json_v6_run_failure_with_fixit.tavern.yaml b/robot-server/tests/integration/http_api/runs/test_json_v6_run_failure_with_fixit.tavern.yaml deleted file mode 100644 index 4365811aedc..00000000000 --- a/robot-server/tests/integration/http_api/runs/test_json_v6_run_failure_with_fixit.tavern.yaml +++ /dev/null @@ -1,124 +0,0 @@ -test_name: Upload and run a JSON v6 protocol that should fail. - -marks: - - usefixtures: - - ot2_server_base_url -stages: - - name: Upload a protocol - request: - url: '{ot2_server_base_url}/protocols' - method: POST - files: - files: 'tests/integration/protocols/simple_v6_failure.json' - response: - status_code: 201 - save: - json: - protocol_id: data.id - - - name: Create run from protocol - request: - url: '{ot2_server_base_url}/runs' - method: POST - json: - data: - protocolId: '{protocol_id}' - response: - status_code: 201 - save: - json: - run_id: data.id - - - name: Play the run - request: - url: '{ot2_server_base_url}/runs/{run_id}/actions' - method: POST - json: - data: - actionType: play - response: - status_code: 201 - strict: - - json:off - - - name: Wait for the run to fail - max_retries: 10 - delay_after: 0.1 - request: - url: '{ot2_server_base_url}/runs/{run_id}' - method: GET - response: - status_code: 200 - strict: - - json:off - json: - data: - status: failed - - - name: Verify the run contains the expected error - request: - url: '{ot2_server_base_url}/runs/{run_id}' - method: GET - response: - status_code: 200 - strict: - - json:off - json: - data: - status: 'awaiting-recovery' - errors: - - id: !anystr - createdAt: !anystr - errorCode: '3005' - errorType: TipNotAttachedError - detail: Pipette should have a tip attached, but does not. - errorInfo: !anydict - wrappedErrors: !anylist - - name: Verify commands contain the expected results - request: - url: '{ot2_server_base_url}/runs/{run_id}/commands' - method: GET - response: - status_code: 200 - json: - links: - current: - href: !anystr - meta: - runId: !anystr - commandId: !anystr - index: 3 - key: !anystr - createdAt: !anystr - meta: - cursor: 3 - totalLength: 4 - data: - - id: !anystr - key: !anystr - commandType: aspirate - createdAt: !anystr - startedAt: !anystr - completedAt: !anystr - status: failed - notes: [] - error: - id: !anystr - errorType: TipNotAttachedError - createdAt: !anystr - detail: Pipette should have a tip attached, but does not. - errorCode: '3005' - errorInfo: !anydict - wrappedErrors: !anylist - params: - pipetteId: pipetteId - labwareId: tipRackId - wellName: A1 - wellLocation: - origin: bottom - offset: - x: 0 - y: 0 - z: 1 - flowRate: 3.78 - volume: 100 \ No newline at end of file