From 07b01f5d58b77aa7c71ae27c4df8e421850e6882 Mon Sep 17 00:00:00 2001 From: tamarzanzouri Date: Thu, 14 Sep 2023 14:45:14 -0400 Subject: [PATCH 01/21] 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 e44a81af0719cd97871912cc1e0ee3d1c68ea195 Mon Sep 17 00:00:00 2001 From: tamarzanzouri Date: Tue, 26 Mar 2024 16:59:41 -0400 Subject: [PATCH 02/21] initial move commands list into task_queue --- .../opentrons/protocol_runner/protocol_runner.py | 14 +++++++++++--- api/src/opentrons/protocol_runner/task_queue.py | 14 +++++++++++--- 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/api/src/opentrons/protocol_runner/protocol_runner.py b/api/src/opentrons/protocol_runner/protocol_runner.py index d2c67b9cfb3..9dfa54cb5ed 100644 --- a/api/src/opentrons/protocol_runner/protocol_runner.py +++ b/api/src/opentrons/protocol_runner/protocol_runner.py @@ -258,6 +258,7 @@ def __init__( ) self._hardware_api.should_taskify_movement_execution(taskify=False) + self._queued_commands: List[pe_commands.CommandCreate] = [] async def load(self, protocol_source: ProtocolSource) -> None: """Load a JSONv6+ ProtocolSource into managed ProtocolEngine.""" @@ -306,10 +307,12 @@ async def load(self, protocol_source: ProtocolSource) -> None: self._protocol_engine.add_command(request=initial_home_command) for command in commands: - self._protocol_engine.add_command(request=command) - await _yield() + self._queued_commands.append(command) - self._task_queue.set_run_func(func=self._protocol_engine.wait_until_complete) + self._task_queue.add_commands_and_execute(self._queued_commands) + self._task_queue.set_run_func( + func=self._protocol_engine.add_and_execute_command + ) async def run( # noqa: D102 self, @@ -322,6 +325,11 @@ async def run( # noqa: D102 if protocol_source: await self.load(protocol_source) + # for command in self._queued_commands: + # await self._protocol_engine.add_and_execute_command(request=command) + # # await self._protocol_engine.wait_for_command(command_id=command.id) + # await _yield() + self.play(deck_configuration=deck_configuration) self._task_queue.start() await self._task_queue.join() diff --git a/api/src/opentrons/protocol_runner/task_queue.py b/api/src/opentrons/protocol_runner/task_queue.py index a24fb0c7c64..f4c43c5af54 100644 --- a/api/src/opentrons/protocol_runner/task_queue.py +++ b/api/src/opentrons/protocol_runner/task_queue.py @@ -2,7 +2,7 @@ import asyncio import logging from functools import partial -from typing import Any, Awaitable, Callable, Optional +from typing import Any, Awaitable, Callable, Optional, List from typing_extensions import Protocol as Callback log = logging.getLogger(__name__) @@ -42,9 +42,10 @@ def __init__( Callable[[Optional[Exception]], Any] ] = None # CleanupFunc = cleanup_func - self._run_func: Optional[Callable[[], Any]] = None + self._run_func: Optional[Callable[[Any], Any]] = None self._run_task: Optional["asyncio.Task[None]"] = None self._ok_to_join_event: asyncio.Event = asyncio.Event() + self._commands_to_execute: List[Any] = [] def set_cleanup_func( self, @@ -68,6 +69,11 @@ def set_run_func( """ self._run_func = partial(func, **kwargs) + def add_commands_and_execute(self, commands: List[Any]) -> None: + print(commands) + for command in commands: + self._commands_to_execute.append(command) + def start(self) -> None: """Start running tasks in the queue.""" self._ok_to_join_event.set() @@ -87,7 +93,9 @@ async def _run(self) -> None: try: if self._run_func is not None: - await self._run_func() + if self._commands_to_execute: + for command in self._commands_to_execute: + await self._run_func(command) except Exception as e: log.exception("Exception raised by protocol") error = e From a07399582fab407d6c1bc203f679ecae802dec28 Mon Sep 17 00:00:00 2001 From: tamarzanzouri Date: Wed, 27 Mar 2024 16:50:09 -0400 Subject: [PATCH 03/21] moved logic from task queue into runner for json protocols --- .../protocol_runner/protocol_runner.py | 45 +++++++++++-------- 1 file changed, 27 insertions(+), 18 deletions(-) diff --git a/api/src/opentrons/protocol_runner/protocol_runner.py b/api/src/opentrons/protocol_runner/protocol_runner.py index 9dfa54cb5ed..a3a4e0dfbe3 100644 --- a/api/src/opentrons/protocol_runner/protocol_runner.py +++ b/api/src/opentrons/protocol_runner/protocol_runner.py @@ -248,14 +248,14 @@ def __init__( self._json_translator = json_translator or JsonTranslator() # TODO(mc, 2022-01-11): replace task queue with specific implementations # of runner interface - self._task_queue = ( - task_queue or TaskQueue() - ) # cleanup_func=protocol_engine.finish)) - self._task_queue.set_cleanup_func( - func=protocol_engine.finish, - drop_tips_after_run=drop_tips_after_run, - post_run_hardware_state=post_run_hardware_state, - ) + # self._task_queue = ( + # task_queue or TaskQueue() + # ) # cleanup_func=protocol_engine.finish)) + # self._task_queue.set_cleanup_func( + # func=protocol_engine.finish, + # drop_tips_after_run=drop_tips_after_run, + # post_run_hardware_state=post_run_hardware_state, + # ) self._hardware_api.should_taskify_movement_execution(taskify=False) self._queued_commands: List[pe_commands.CommandCreate] = [] @@ -309,10 +309,10 @@ async def load(self, protocol_source: ProtocolSource) -> None: for command in commands: self._queued_commands.append(command) - self._task_queue.add_commands_and_execute(self._queued_commands) - self._task_queue.set_run_func( - func=self._protocol_engine.add_and_execute_command - ) + # self._task_queue.add_commands_and_execute(self._queued_commands) + # self._task_queue.set_run_func( + # func=self._protocol_engine.add_and_execute_command + # ) async def run( # noqa: D102 self, @@ -325,19 +325,28 @@ async def run( # noqa: D102 if protocol_source: await self.load(protocol_source) - # for command in self._queued_commands: - # await self._protocol_engine.add_and_execute_command(request=command) - # # await self._protocol_engine.wait_for_command(command_id=command.id) - # await _yield() + asyncio.create_task(self._run()) self.play(deck_configuration=deck_configuration) - self._task_queue.start() - await self._task_queue.join() + # self._task_queue.start() + # await self._task_queue.join() run_data = self._protocol_engine.state_view.get_summary() commands = self._protocol_engine.state_view.commands.get_all() return RunResult(commands=commands, state_summary=run_data) + async def _run(self) -> None: + error = None + + try: + for command in self._queued_commands: + await self._protocol_engine.add_and_execute_command(command) + except Exception as e: + # log.exception("Exception raised by protocol") + error = e + + await self._protocol_engine.finish(error) + class LiveRunner(AbstractRunner): """Protocol runner implementation for live http protocols.""" From ce4381968addf307e03731a92695888a3ac3f4bb Mon Sep 17 00:00:00 2001 From: tamarzanzouri Date: Wed, 27 Mar 2024 16:56:20 -0400 Subject: [PATCH 04/21] reverted task queue changes --- api/src/opentrons/protocol_runner/task_queue.py | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/api/src/opentrons/protocol_runner/task_queue.py b/api/src/opentrons/protocol_runner/task_queue.py index f4c43c5af54..a24fb0c7c64 100644 --- a/api/src/opentrons/protocol_runner/task_queue.py +++ b/api/src/opentrons/protocol_runner/task_queue.py @@ -2,7 +2,7 @@ import asyncio import logging from functools import partial -from typing import Any, Awaitable, Callable, Optional, List +from typing import Any, Awaitable, Callable, Optional from typing_extensions import Protocol as Callback log = logging.getLogger(__name__) @@ -42,10 +42,9 @@ def __init__( Callable[[Optional[Exception]], Any] ] = None # CleanupFunc = cleanup_func - self._run_func: Optional[Callable[[Any], Any]] = None + self._run_func: Optional[Callable[[], Any]] = None self._run_task: Optional["asyncio.Task[None]"] = None self._ok_to_join_event: asyncio.Event = asyncio.Event() - self._commands_to_execute: List[Any] = [] def set_cleanup_func( self, @@ -69,11 +68,6 @@ def set_run_func( """ self._run_func = partial(func, **kwargs) - def add_commands_and_execute(self, commands: List[Any]) -> None: - print(commands) - for command in commands: - self._commands_to_execute.append(command) - def start(self) -> None: """Start running tasks in the queue.""" self._ok_to_join_event.set() @@ -93,9 +87,7 @@ async def _run(self) -> None: try: if self._run_func is not None: - if self._commands_to_execute: - for command in self._commands_to_execute: - await self._run_func(command) + await self._run_func() except Exception as e: log.exception("Exception raised by protocol") error = e From 004e49842d9ffbe11bceade4f20e0c67ede5272a Mon Sep 17 00:00:00 2001 From: tamarzanzouri Date: Thu, 28 Mar 2024 15:52:37 -0400 Subject: [PATCH 05/21] json runner with Event set and wait. fixed v6 upload test --- .../protocol_runner/protocol_runner.py | 14 +- .../test_json_v6_protocol_run.tavern.yaml | 394 +++++++++--------- 2 files changed, 210 insertions(+), 198 deletions(-) diff --git a/api/src/opentrons/protocol_runner/protocol_runner.py b/api/src/opentrons/protocol_runner/protocol_runner.py index a3a4e0dfbe3..5356df1cdfb 100644 --- a/api/src/opentrons/protocol_runner/protocol_runner.py +++ b/api/src/opentrons/protocol_runner/protocol_runner.py @@ -1,5 +1,6 @@ """Protocol run control and management.""" import asyncio +import logging from typing import List, NamedTuple, Optional, Union from abc import ABC, abstractmethod @@ -41,6 +42,8 @@ RunTimeParamValuesType, ) +log = logging.getLogger(__name__) + class RunResult(NamedTuple): """Result data from a run, pulled from the ProtocolEngine.""" @@ -325,9 +328,18 @@ async def run( # noqa: D102 if protocol_source: await self.load(protocol_source) - asyncio.create_task(self._run()) + _ok_to_join_event = asyncio.Event() + + _ok_to_join_event.set() + + run_task = asyncio.create_task(self._run()) self.play(deck_configuration=deck_configuration) + + _ok_to_join_event.wait() + + await run_task + # self._task_queue.start() # await self._task_queue.join() diff --git a/robot-server/tests/integration/http_api/runs/test_json_v6_protocol_run.tavern.yaml b/robot-server/tests/integration/http_api/runs/test_json_v6_protocol_run.tavern.yaml index 1118d9a8870..bf9017b796e 100644 --- a/robot-server/tests/integration/http_api/runs/test_json_v6_protocol_run.tavern.yaml +++ b/robot-server/tests/integration/http_api/runs/test_json_v6_protocol_run.tavern.yaml @@ -93,10 +93,10 @@ stages: commandId: '{setup_command_id}' key: '{setup_command_key}' createdAt: '{setup_command_created_at}' - index: 14 + index: 1 meta: cursor: 0 - totalLength: 15 + totalLength: 2 data: # Initial home - id: !anystr @@ -105,184 +105,184 @@ stages: createdAt: !anystr status: queued params: { } - - id: !anystr - key: !anystr - commandType: loadPipette - createdAt: !anystr - status: queued - params: - pipetteName: p10_single - mount: left - pipetteId: pipetteId - - id: !anystr - key: !anystr - commandType: loadModule - createdAt: !anystr - status: queued - params: - model: magneticModuleV1 - location: - slotName: '3' - moduleId: magneticModuleId - - id: !anystr - key: !anystr - commandType: loadModule - createdAt: !anystr - status: queued - params: - model: temperatureModuleV2 - location: - slotName: '1' - moduleId: temperatureModuleId - - id: !anystr - key: !anystr - commandType: loadLabware - createdAt: !anystr - status: queued - params: - location: - moduleId: temperatureModuleId - loadName: foo_8_plate_33ul - namespace: example - version: 1 - labwareId: sourcePlateId - displayName: Source Plate - - id: !anystr - key: !anystr - commandType: loadLabware - createdAt: !anystr - status: queued - params: - location: - moduleId: magneticModuleId - loadName: foo_8_plate_33ul - namespace: example - version: 1 - labwareId: destPlateId - displayName: Sample Collection Plate - - id: !anystr - key: !anystr - commandType: loadLabware - createdAt: !anystr - status: queued - params: - location: - slotName: '8' - loadName: opentrons_96_tiprack_10ul - namespace: opentrons - version: 1 - labwareId: tipRackId - displayName: Opentrons 96 Tip Rack 10 µL - - id: !anystr - createdAt: !anystr - commandType: loadLiquid - key: !anystr - status: queued - params: - liquidId: 'waterId' - labwareId: 'sourcePlateId' - volumeByWell: - A1: 100 - B1: 100 - - id: !anystr - key: !anystr - commandType: pickUpTip - createdAt: !anystr - status: queued - params: - pipetteId: pipetteId - labwareId: tipRackId - wellName: B1 - wellLocation: - origin: top - offset: - x: 0 - 'y': 0 - z: 0 - - id: !anystr - key: !anystr - commandType: aspirate - createdAt: !anystr - status: queued - params: - pipetteId: pipetteId - labwareId: sourcePlateId - wellName: A1 - wellLocation: - origin: bottom - offset: - x: 0 - 'y': 0 - z: 2 - volume: 5 - flowRate: 3 - - id: !anystr - key: !anystr - commandType: dispense - createdAt: !anystr - status: queued - params: - pipetteId: pipetteId - labwareId: destPlateId - wellName: B1 - wellLocation: - origin: bottom - offset: - x: 0 - 'y': 0 - z: 1 - volume: 4.5 - flowRate: 2.5 - - id: !anystr - key: !anystr - commandType: moveToWell - createdAt: !anystr - status: queued - params: - pipetteId: pipetteId - labwareId: destPlateId - wellName: B2 - wellLocation: - origin: top - offset: - x: 0 - 'y': 0 - z: 0 - forceDirect: false - - id: !anystr - key: !anystr - commandType: moveToWell - createdAt: !anystr - status: queued - params: - pipetteId: pipetteId - labwareId: destPlateId - wellName: B2 - wellLocation: - origin: bottom - offset: - x: 2 - y: 3 - z: 10 - minimumZHeight: 35 - forceDirect: true - speed: 12.3 - - id: !anystr - key: !anystr - commandType: dropTip - createdAt: !anystr - status: queued - params: - pipetteId: pipetteId - labwareId: fixedTrash - wellName: A1 - wellLocation: - origin: default - offset: - x: 0 - y: 0 - z: 0 - alternateDropLocation: false +# - id: !anystr +# key: !anystr +# commandType: loadPipette +# createdAt: !anystr +# status: queued +# params: +# pipetteName: p10_single +# mount: left +# pipetteId: pipetteId +# - id: !anystr +# key: !anystr +# commandType: loadModule +# createdAt: !anystr +# status: queued +# params: +# model: magneticModuleV1 +# location: +# slotName: '3' +# moduleId: magneticModuleId +# - id: !anystr +# key: !anystr +# commandType: loadModule +# createdAt: !anystr +# status: queued +# params: +# model: temperatureModuleV2 +# location: +# slotName: '1' +# moduleId: temperatureModuleId +# - id: !anystr +# key: !anystr +# commandType: loadLabware +# createdAt: !anystr +# status: queued +# params: +# location: +# moduleId: temperatureModuleId +# loadName: foo_8_plate_33ul +# namespace: example +# version: 1 +# labwareId: sourcePlateId +# displayName: Source Plate +# - id: !anystr +# key: !anystr +# commandType: loadLabware +# createdAt: !anystr +# status: queued +# params: +# location: +# moduleId: magneticModuleId +# loadName: foo_8_plate_33ul +# namespace: example +# version: 1 +# labwareId: destPlateId +# displayName: Sample Collection Plate +# - id: !anystr +# key: !anystr +# commandType: loadLabware +# createdAt: !anystr +# status: queued +# params: +# location: +# slotName: '8' +# loadName: opentrons_96_tiprack_10ul +# namespace: opentrons +# version: 1 +# labwareId: tipRackId +# displayName: Opentrons 96 Tip Rack 10 µL +# - id: !anystr +# createdAt: !anystr +# commandType: loadLiquid +# key: !anystr +# status: queued +# params: +# liquidId: 'waterId' +# labwareId: 'sourcePlateId' +# volumeByWell: +# A1: 100 +# B1: 100 +# - id: !anystr +# key: !anystr +# commandType: pickUpTip +# createdAt: !anystr +# status: queued +# params: +# pipetteId: pipetteId +# labwareId: tipRackId +# wellName: B1 +# wellLocation: +# origin: top +# offset: +# x: 0 +# 'y': 0 +# z: 0 +# - id: !anystr +# key: !anystr +# commandType: aspirate +# createdAt: !anystr +# status: queued +# params: +# pipetteId: pipetteId +# labwareId: sourcePlateId +# wellName: A1 +# wellLocation: +# origin: bottom +# offset: +# x: 0 +# 'y': 0 +# z: 2 +# volume: 5 +# flowRate: 3 +# - id: !anystr +# key: !anystr +# commandType: dispense +# createdAt: !anystr +# status: queued +# params: +# pipetteId: pipetteId +# labwareId: destPlateId +# wellName: B1 +# wellLocation: +# origin: bottom +# offset: +# x: 0 +# 'y': 0 +# z: 1 +# volume: 4.5 +# flowRate: 2.5 +# - id: !anystr +# key: !anystr +# commandType: moveToWell +# createdAt: !anystr +# status: queued +# params: +# pipetteId: pipetteId +# labwareId: destPlateId +# wellName: B2 +# wellLocation: +# origin: top +# offset: +# x: 0 +# 'y': 0 +# z: 0 +# forceDirect: false +# - id: !anystr +# key: !anystr +# commandType: moveToWell +# createdAt: !anystr +# status: queued +# params: +# pipetteId: pipetteId +# labwareId: destPlateId +# wellName: B2 +# wellLocation: +# origin: bottom +# offset: +# x: 2 +# y: 3 +# z: 10 +# minimumZHeight: 35 +# forceDirect: true +# speed: 12.3 +# - id: !anystr +# key: !anystr +# commandType: dropTip +# createdAt: !anystr +# status: queued +# params: +# pipetteId: pipetteId +# labwareId: fixedTrash +# wellName: A1 +# wellLocation: +# origin: default +# offset: +# x: 0 +# y: 0 +# z: 0 +# alternateDropLocation: false - id: '{setup_command_id}' key: '{setup_command_key}' intent: setup @@ -350,6 +350,15 @@ stages: params: { } startedAt: !anystr completedAt: !anystr + - id: '{setup_command_id}' + key: '{setup_command_key}' + intent: setup + commandType: home + createdAt: '{setup_command_created_at}' + startedAt: '{setup_command_started_at}' + completedAt: '{setup_command_completed_at}' + status: succeeded + params: { } - id: !anystr key: !anystr commandType: loadPipette @@ -554,15 +563,6 @@ stages: y: 0 z: 0 alternateDropLocation: false - - id: '{setup_command_id}' - key: '{setup_command_key}' - intent: setup - commandType: home - createdAt: '{setup_command_created_at}' - startedAt: '{setup_command_started_at}' - completedAt: '{setup_command_completed_at}' - status: succeeded - params: {} - name: Verify commands succeeded with pageLength and cursor request: @@ -593,12 +593,12 @@ stages: status: succeeded params: location: - moduleId: magneticModuleId + moduleId: temperatureModuleId loadName: foo_8_plate_33ul namespace: example version: 1 - labwareId: destPlateId - displayName: Sample Collection Plate + labwareId: sourcePlateId + displayName: Source Plate - id: !anystr key: !anystr commandType: loadLabware @@ -608,9 +608,9 @@ stages: status: succeeded params: location: - slotName: '8' - loadName: opentrons_96_tiprack_10ul - namespace: opentrons + moduleId: magneticModuleId + loadName: foo_8_plate_33ul + namespace: example version: 1 - labwareId: tipRackId - displayName: Opentrons 96 Tip Rack 10 µL + labwareId: destPlateId + displayName: Sample Collection Plate From e2d4658b4a31d1a8e0a861c5ec87888adbfed254 Mon Sep 17 00:00:00 2001 From: tamarzanzouri Date: Fri, 29 Mar 2024 16:11:50 -0400 Subject: [PATCH 06/21] reverted task queue changes and use runner._run method instead --- .../protocol_runner/protocol_runner.py | 56 ++--- .../protocol_runner/test_protocol_runner.py | 19 +- .../runs/test_json_v6_run_failure.tavern.yaml | 36 ++-- .../test_json_v7_protocol_run.tavern.yaml | 200 +----------------- 4 files changed, 52 insertions(+), 259 deletions(-) diff --git a/api/src/opentrons/protocol_runner/protocol_runner.py b/api/src/opentrons/protocol_runner/protocol_runner.py index 5356df1cdfb..ba884e67ad7 100644 --- a/api/src/opentrons/protocol_runner/protocol_runner.py +++ b/api/src/opentrons/protocol_runner/protocol_runner.py @@ -36,6 +36,7 @@ LegacyExecutor, LegacyLoadInfo, ) +from ..protocol_engine.errors import ProtocolCommandFailedError from ..protocol_engine.types import ( PostRunHardwareState, DeckConfigurationType, @@ -251,14 +252,14 @@ def __init__( self._json_translator = json_translator or JsonTranslator() # TODO(mc, 2022-01-11): replace task queue with specific implementations # of runner interface - # self._task_queue = ( - # task_queue or TaskQueue() - # ) # cleanup_func=protocol_engine.finish)) - # self._task_queue.set_cleanup_func( - # func=protocol_engine.finish, - # drop_tips_after_run=drop_tips_after_run, - # post_run_hardware_state=post_run_hardware_state, - # ) + self._task_queue = ( + task_queue or TaskQueue() + ) # cleanup_func=protocol_engine.finish)) + self._task_queue.set_cleanup_func( + func=protocol_engine.finish, + drop_tips_after_run=drop_tips_after_run, + post_run_hardware_state=post_run_hardware_state, + ) self._hardware_api.should_taskify_movement_execution(taskify=False) self._queued_commands: List[pe_commands.CommandCreate] = [] @@ -303,6 +304,7 @@ async def load(self, protocol_source: ProtocolSource) -> None: color=liquid.displayColor, ) await _yield() + initial_home_command = pe_commands.HomeCreate( params=pe_commands.HomeParams(axes=None) ) @@ -312,10 +314,7 @@ async def load(self, protocol_source: ProtocolSource) -> None: for command in commands: self._queued_commands.append(command) - # self._task_queue.add_commands_and_execute(self._queued_commands) - # self._task_queue.set_run_func( - # func=self._protocol_engine.add_and_execute_command - # ) + self._task_queue.set_run_func(func=self._run) async def run( # noqa: D102 self, @@ -328,36 +327,25 @@ async def run( # noqa: D102 if protocol_source: await self.load(protocol_source) - _ok_to_join_event = asyncio.Event() - - _ok_to_join_event.set() - - run_task = asyncio.create_task(self._run()) - self.play(deck_configuration=deck_configuration) - - _ok_to_join_event.wait() - - await run_task - - # self._task_queue.start() - # await self._task_queue.join() + self._task_queue.start() + await self._task_queue.join() run_data = self._protocol_engine.state_view.get_summary() commands = self._protocol_engine.state_view.commands.get_all() return RunResult(commands=commands, state_summary=run_data) async def _run(self) -> None: - error = None - - try: - for command in self._queued_commands: - await self._protocol_engine.add_and_execute_command(command) - except Exception as e: - # log.exception("Exception raised by protocol") - error = e + for command in self._queued_commands: + result = await self._protocol_engine.add_and_execute_command(command) + if result.error: + log.exception("Exception raised by protocol") + raise ProtocolCommandFailedError( + original_error=result.error, + message=f"{result.error.errorType}: {result.error.detail}", + ) - await self._protocol_engine.finish(error) + # await self._protocol_engine.finish(error) class LiveRunner(AbstractRunner): diff --git a/api/tests/opentrons/protocol_runner/test_protocol_runner.py b/api/tests/opentrons/protocol_runner/test_protocol_runner.py index 64034e663bd..39e603b2abc 100644 --- a/api/tests/opentrons/protocol_runner/test_protocol_runner.py +++ b/api/tests/opentrons/protocol_runner/test_protocol_runner.py @@ -393,24 +393,7 @@ async def test_load_json_runner( protocol_engine.add_command( request=pe_commands.HomeCreate(params=pe_commands.HomeParams(axes=None)) ), - protocol_engine.add_command( - request=pe_commands.WaitForResumeCreate( - params=pe_commands.WaitForResumeParams(message="hello") - ) - ), - protocol_engine.add_command( - request=pe_commands.WaitForResumeCreate( - params=pe_commands.WaitForResumeParams(message="goodbye") - ) - ), - protocol_engine.add_command( - request=pe_commands.LoadLiquidCreate( - params=pe_commands.LoadLiquidParams( - liquidId="water-id", labwareId="labware-id", volumeByWell={"A1": 30} - ) - ), - ), - task_queue.set_run_func(func=protocol_engine.wait_until_complete), + task_queue.set_run_func(func=json_runner_subject._run), ) diff --git a/robot-server/tests/integration/http_api/runs/test_json_v6_run_failure.tavern.yaml b/robot-server/tests/integration/http_api/runs/test_json_v6_run_failure.tavern.yaml index d9266dff9b0..6d3d2806ae1 100644 --- a/robot-server/tests/integration/http_api/runs/test_json_v6_run_failure.tavern.yaml +++ b/robot-server/tests/integration/http_api/runs/test_json_v6_run_failure.tavern.yaml @@ -86,12 +86,12 @@ stages: meta: runId: !anystr commandId: !anystr - index: 4 + index: 3 key: !anystr createdAt: !anystr meta: cursor: 3 - totalLength: 5 + totalLength: 4 data: - id: !anystr key: !anystr @@ -120,19 +120,19 @@ stages: z: 1 flowRate: 3.78 volume: 100 - - id: !anystr - key: !anystr - commandType: pickUpTip - createdAt: !anystr - completedAt: !anystr - status: failed - params: - pipetteId: pipetteId - labwareId: tipRackId - wellName: A1 - wellLocation: - origin: top - offset: - x: 0 - y: 0 - z: 0 +# - id: !anystr +# key: !anystr +# commandType: pickUpTip +# createdAt: !anystr +# completedAt: !anystr +# status: failed +# params: +# pipetteId: pipetteId +# labwareId: tipRackId +# wellName: A1 +# wellLocation: +# origin: top +# offset: +# x: 0 +# y: 0 +# z: 0 diff --git a/robot-server/tests/integration/http_api/runs/test_json_v7_protocol_run.tavern.yaml b/robot-server/tests/integration/http_api/runs/test_json_v7_protocol_run.tavern.yaml index ace0c47cf64..98e8bc9579b 100644 --- a/robot-server/tests/integration/http_api/runs/test_json_v7_protocol_run.tavern.yaml +++ b/robot-server/tests/integration/http_api/runs/test_json_v7_protocol_run.tavern.yaml @@ -93,10 +93,10 @@ stages: commandId: '{setup_command_id}' key: '{setup_command_key}' createdAt: '{setup_command_created_at}' - index: 14 + index: 1 meta: cursor: 0 - totalLength: 15 + totalLength: 2 data: # Initial home - id: !anystr @@ -105,184 +105,6 @@ stages: createdAt: !anystr status: queued params: { } - - id: !anystr - key: !anystr - commandType: loadPipette - createdAt: !anystr - status: queued - params: - pipetteName: p10_single - mount: left - pipetteId: pipetteId - - id: !anystr - key: !anystr - commandType: loadModule - createdAt: !anystr - status: queued - params: - model: magneticModuleV1 - location: - slotName: '3' - moduleId: magneticModuleId - - id: !anystr - key: !anystr - commandType: loadModule - createdAt: !anystr - status: queued - params: - model: temperatureModuleV2 - location: - slotName: '1' - moduleId: temperatureModuleId - - id: !anystr - key: !anystr - commandType: loadLabware - createdAt: !anystr - status: queued - params: - location: - moduleId: temperatureModuleId - loadName: foo_8_plate_33ul - namespace: example - version: 1 - labwareId: sourcePlateId - displayName: Source Plate - - id: !anystr - key: !anystr - commandType: loadLabware - createdAt: !anystr - status: queued - params: - location: - moduleId: magneticModuleId - loadName: foo_8_plate_33ul - namespace: example - version: 1 - labwareId: destPlateId - displayName: Sample Collection Plate - - id: !anystr - key: !anystr - commandType: loadLabware - createdAt: !anystr - status: queued - params: - location: - slotName: '8' - loadName: opentrons_96_tiprack_10ul - namespace: opentrons - version: 1 - labwareId: tipRackId - displayName: Opentrons 96 Tip Rack 10 µL - - id: !anystr - createdAt: !anystr - commandType: loadLiquid - key: !anystr - status: queued - params: - liquidId: 'waterId' - labwareId: 'sourcePlateId' - volumeByWell: - A1: 100 - B1: 100 - - id: !anystr - key: !anystr - commandType: pickUpTip - createdAt: !anystr - status: queued - params: - pipetteId: pipetteId - labwareId: tipRackId - wellName: B1 - wellLocation: - origin: top - offset: - x: 0 - 'y': 0 - z: 0 - - id: !anystr - key: !anystr - commandType: aspirate - createdAt: !anystr - status: queued - params: - pipetteId: pipetteId - labwareId: sourcePlateId - wellName: A1 - wellLocation: - origin: bottom - offset: - x: 0 - 'y': 0 - z: 2 - volume: 5 - flowRate: 3 - - id: !anystr - key: !anystr - commandType: dispense - createdAt: !anystr - status: queued - params: - pipetteId: pipetteId - labwareId: destPlateId - wellName: B1 - wellLocation: - origin: bottom - offset: - x: 0 - 'y': 0 - z: 1 - volume: 4.5 - flowRate: 2.5 - - id: !anystr - key: !anystr - commandType: moveToWell - createdAt: !anystr - status: queued - params: - pipetteId: pipetteId - labwareId: destPlateId - wellName: B2 - wellLocation: - origin: top - offset: - x: 0 - 'y': 0 - z: 0 - forceDirect: false - - id: !anystr - key: !anystr - commandType: moveToWell - createdAt: !anystr - status: queued - params: - pipetteId: pipetteId - labwareId: destPlateId - wellName: B2 - wellLocation: - origin: bottom - offset: - x: 2 - y: 3 - z: 10 - minimumZHeight: 35 - forceDirect: true - speed: 12.3 - - id: !anystr - key: !anystr - commandType: dropTip - createdAt: !anystr - status: queued - params: - pipetteId: pipetteId - labwareId: fixedTrash - wellName: A1 - wellLocation: - origin: default - offset: - x: 0 - y: 0 - z: 0 - alternateDropLocation: false - id: '{setup_command_id}' key: '{setup_command_key}' intent: setup @@ -350,6 +172,15 @@ stages: completedAt: !anystr status: succeeded params: { } + - id: '{setup_command_id}' + key: '{setup_command_key}' + intent: setup + commandType: home + createdAt: '{setup_command_created_at}' + startedAt: '{setup_command_started_at}' + completedAt: '{setup_command_completed_at}' + status: succeeded + params: { } - id: !anystr key: !anystr commandType: loadPipette @@ -554,12 +385,3 @@ stages: y: 0 z: 0 alternateDropLocation: false - - id: '{setup_command_id}' - key: '{setup_command_key}' - intent: setup - commandType: home - createdAt: '{setup_command_created_at}' - startedAt: '{setup_command_started_at}' - completedAt: '{setup_command_completed_at}' - status: succeeded - params: { } From f87b1db931576f6a2a519424431657786dd778c4 Mon Sep 17 00:00:00 2001 From: TamarZanzouri Date: Fri, 29 Mar 2024 16:23:44 -0400 Subject: [PATCH 07/21] Delete shared-data/fixture/schemas/1.json --- shared-data/fixture/schemas/1.json | 149 ----------------------------- 1 file changed, 149 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 647d49a5ac3..00000000000 --- a/shared-data/fixture/schemas/1.json +++ /dev/null @@ -1,149 +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 3d7c688ce34feef4cc71fa0f92197a62a24e5e9a Mon Sep 17 00:00:00 2001 From: tamarzanzouri Date: Fri, 29 Mar 2024 16:27:10 -0400 Subject: [PATCH 08/21] clean up --- .../protocol_runner/protocol_runner.py | 10 +- .../protocol_runner/test_protocol_runner.py | 2 +- .../test_json_v6_protocol_run.tavern.yaml | 178 ------------------ .../runs/test_json_v6_run_failure.tavern.yaml | 18 +- 4 files changed, 4 insertions(+), 204 deletions(-) diff --git a/api/src/opentrons/protocol_runner/protocol_runner.py b/api/src/opentrons/protocol_runner/protocol_runner.py index ba884e67ad7..a94eec262bd 100644 --- a/api/src/opentrons/protocol_runner/protocol_runner.py +++ b/api/src/opentrons/protocol_runner/protocol_runner.py @@ -1,6 +1,5 @@ """Protocol run control and management.""" import asyncio -import logging from typing import List, NamedTuple, Optional, Union from abc import ABC, abstractmethod @@ -43,8 +42,6 @@ RunTimeParamValuesType, ) -log = logging.getLogger(__name__) - class RunResult(NamedTuple): """Result data from a run, pulled from the ProtocolEngine.""" @@ -314,7 +311,7 @@ async def load(self, protocol_source: ProtocolSource) -> None: for command in commands: self._queued_commands.append(command) - self._task_queue.set_run_func(func=self._run) + self._task_queue.set_run_func(func=self._add_command_and_execute) async def run( # noqa: D102 self, @@ -335,18 +332,15 @@ async def run( # noqa: D102 commands = self._protocol_engine.state_view.commands.get_all() return RunResult(commands=commands, state_summary=run_data) - async def _run(self) -> None: + async def _add_command_and_execute(self) -> None: for command in self._queued_commands: result = await self._protocol_engine.add_and_execute_command(command) if result.error: - log.exception("Exception raised by protocol") raise ProtocolCommandFailedError( original_error=result.error, message=f"{result.error.errorType}: {result.error.detail}", ) - # await self._protocol_engine.finish(error) - class LiveRunner(AbstractRunner): """Protocol runner implementation for live http protocols.""" diff --git a/api/tests/opentrons/protocol_runner/test_protocol_runner.py b/api/tests/opentrons/protocol_runner/test_protocol_runner.py index 39e603b2abc..af25e4540e5 100644 --- a/api/tests/opentrons/protocol_runner/test_protocol_runner.py +++ b/api/tests/opentrons/protocol_runner/test_protocol_runner.py @@ -393,7 +393,7 @@ async def test_load_json_runner( protocol_engine.add_command( request=pe_commands.HomeCreate(params=pe_commands.HomeParams(axes=None)) ), - task_queue.set_run_func(func=json_runner_subject._run), + task_queue.set_run_func(func=json_runner_subject._add_command_and_execute), ) diff --git a/robot-server/tests/integration/http_api/runs/test_json_v6_protocol_run.tavern.yaml b/robot-server/tests/integration/http_api/runs/test_json_v6_protocol_run.tavern.yaml index bf9017b796e..319fb612f9e 100644 --- a/robot-server/tests/integration/http_api/runs/test_json_v6_protocol_run.tavern.yaml +++ b/robot-server/tests/integration/http_api/runs/test_json_v6_protocol_run.tavern.yaml @@ -105,184 +105,6 @@ stages: createdAt: !anystr status: queued params: { } -# - id: !anystr -# key: !anystr -# commandType: loadPipette -# createdAt: !anystr -# status: queued -# params: -# pipetteName: p10_single -# mount: left -# pipetteId: pipetteId -# - id: !anystr -# key: !anystr -# commandType: loadModule -# createdAt: !anystr -# status: queued -# params: -# model: magneticModuleV1 -# location: -# slotName: '3' -# moduleId: magneticModuleId -# - id: !anystr -# key: !anystr -# commandType: loadModule -# createdAt: !anystr -# status: queued -# params: -# model: temperatureModuleV2 -# location: -# slotName: '1' -# moduleId: temperatureModuleId -# - id: !anystr -# key: !anystr -# commandType: loadLabware -# createdAt: !anystr -# status: queued -# params: -# location: -# moduleId: temperatureModuleId -# loadName: foo_8_plate_33ul -# namespace: example -# version: 1 -# labwareId: sourcePlateId -# displayName: Source Plate -# - id: !anystr -# key: !anystr -# commandType: loadLabware -# createdAt: !anystr -# status: queued -# params: -# location: -# moduleId: magneticModuleId -# loadName: foo_8_plate_33ul -# namespace: example -# version: 1 -# labwareId: destPlateId -# displayName: Sample Collection Plate -# - id: !anystr -# key: !anystr -# commandType: loadLabware -# createdAt: !anystr -# status: queued -# params: -# location: -# slotName: '8' -# loadName: opentrons_96_tiprack_10ul -# namespace: opentrons -# version: 1 -# labwareId: tipRackId -# displayName: Opentrons 96 Tip Rack 10 µL -# - id: !anystr -# createdAt: !anystr -# commandType: loadLiquid -# key: !anystr -# status: queued -# params: -# liquidId: 'waterId' -# labwareId: 'sourcePlateId' -# volumeByWell: -# A1: 100 -# B1: 100 -# - id: !anystr -# key: !anystr -# commandType: pickUpTip -# createdAt: !anystr -# status: queued -# params: -# pipetteId: pipetteId -# labwareId: tipRackId -# wellName: B1 -# wellLocation: -# origin: top -# offset: -# x: 0 -# 'y': 0 -# z: 0 -# - id: !anystr -# key: !anystr -# commandType: aspirate -# createdAt: !anystr -# status: queued -# params: -# pipetteId: pipetteId -# labwareId: sourcePlateId -# wellName: A1 -# wellLocation: -# origin: bottom -# offset: -# x: 0 -# 'y': 0 -# z: 2 -# volume: 5 -# flowRate: 3 -# - id: !anystr -# key: !anystr -# commandType: dispense -# createdAt: !anystr -# status: queued -# params: -# pipetteId: pipetteId -# labwareId: destPlateId -# wellName: B1 -# wellLocation: -# origin: bottom -# offset: -# x: 0 -# 'y': 0 -# z: 1 -# volume: 4.5 -# flowRate: 2.5 -# - id: !anystr -# key: !anystr -# commandType: moveToWell -# createdAt: !anystr -# status: queued -# params: -# pipetteId: pipetteId -# labwareId: destPlateId -# wellName: B2 -# wellLocation: -# origin: top -# offset: -# x: 0 -# 'y': 0 -# z: 0 -# forceDirect: false -# - id: !anystr -# key: !anystr -# commandType: moveToWell -# createdAt: !anystr -# status: queued -# params: -# pipetteId: pipetteId -# labwareId: destPlateId -# wellName: B2 -# wellLocation: -# origin: bottom -# offset: -# x: 2 -# y: 3 -# z: 10 -# minimumZHeight: 35 -# forceDirect: true -# speed: 12.3 -# - id: !anystr -# key: !anystr -# commandType: dropTip -# createdAt: !anystr -# status: queued -# params: -# pipetteId: pipetteId -# labwareId: fixedTrash -# wellName: A1 -# wellLocation: -# origin: default -# offset: -# x: 0 -# y: 0 -# z: 0 -# alternateDropLocation: false - id: '{setup_command_id}' key: '{setup_command_key}' intent: setup diff --git a/robot-server/tests/integration/http_api/runs/test_json_v6_run_failure.tavern.yaml b/robot-server/tests/integration/http_api/runs/test_json_v6_run_failure.tavern.yaml index 6d3d2806ae1..abfe325167f 100644 --- a/robot-server/tests/integration/http_api/runs/test_json_v6_run_failure.tavern.yaml +++ b/robot-server/tests/integration/http_api/runs/test_json_v6_run_failure.tavern.yaml @@ -119,20 +119,4 @@ stages: y: 0 z: 1 flowRate: 3.78 - volume: 100 -# - id: !anystr -# key: !anystr -# commandType: pickUpTip -# createdAt: !anystr -# completedAt: !anystr -# status: failed -# params: -# pipetteId: pipetteId -# labwareId: tipRackId -# wellName: A1 -# wellLocation: -# origin: top -# offset: -# x: 0 -# y: 0 -# z: 0 + volume: 100 \ No newline at end of file From 747974cc8c6a735589739e2deafee9d6bf406ff6 Mon Sep 17 00:00:00 2001 From: tamarzanzouri Date: Mon, 1 Apr 2024 16:11:34 -0400 Subject: [PATCH 09/21] fixed failing test from merge --- .../http_api/runs/test_json_v6_protocol_run.tavern.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/robot-server/tests/integration/http_api/runs/test_json_v6_protocol_run.tavern.yaml b/robot-server/tests/integration/http_api/runs/test_json_v6_protocol_run.tavern.yaml index 9c394bbb1d8..4ff631bf277 100644 --- a/robot-server/tests/integration/http_api/runs/test_json_v6_protocol_run.tavern.yaml +++ b/robot-server/tests/integration/http_api/runs/test_json_v6_protocol_run.tavern.yaml @@ -104,7 +104,7 @@ stages: commandType: home createdAt: !anystr status: queued - params: { } + params: {} - id: '{setup_command_id}' key: '{setup_command_key}' intent: setup @@ -183,6 +183,7 @@ stages: completedAt: '{setup_command_completed_at}' status: succeeded params: { } + notes: [] - id: !anystr key: !anystr commandType: loadPipette From bd2afe5ea4c285c12069821a2c48ff5612ec9448 Mon Sep 17 00:00:00 2001 From: tamarzanzouri Date: Mon, 1 Apr 2024 16:16:05 -0400 Subject: [PATCH 10/21] fixed failing test --- .../http_api/runs/test_json_v7_protocol_run.tavern.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/robot-server/tests/integration/http_api/runs/test_json_v7_protocol_run.tavern.yaml b/robot-server/tests/integration/http_api/runs/test_json_v7_protocol_run.tavern.yaml index 7a062456e8f..317d339fbbf 100644 --- a/robot-server/tests/integration/http_api/runs/test_json_v7_protocol_run.tavern.yaml +++ b/robot-server/tests/integration/http_api/runs/test_json_v7_protocol_run.tavern.yaml @@ -173,6 +173,7 @@ stages: completedAt: !anystr status: succeeded params: { } + notes: [ ] - id: '{setup_command_id}' key: '{setup_command_key}' intent: setup From 335c10d73c7225234eb2a6d98a3d0dd18d65da42 Mon Sep 17 00:00:00 2001 From: tamarzanzouri Date: Mon, 1 Apr 2024 21:50:28 -0400 Subject: [PATCH 11/21] started adding tests WIP --- .../protocol_runner/protocol_runner.py | 6 +-- .../protocol_runner/test_protocol_runner.py | 50 ++++++++++++++++++- 2 files changed, 51 insertions(+), 5 deletions(-) diff --git a/api/src/opentrons/protocol_runner/protocol_runner.py b/api/src/opentrons/protocol_runner/protocol_runner.py index 9d6561e8e2c..6e8a10e26a1 100644 --- a/api/src/opentrons/protocol_runner/protocol_runner.py +++ b/api/src/opentrons/protocol_runner/protocol_runner.py @@ -333,8 +333,7 @@ async def load(self, protocol_source: ProtocolSource) -> None: # this command homes all axes, including pipette plugner and gripper jaw self._protocol_engine.add_command(request=initial_home_command) - for command in commands: - self._queued_commands.append(command) + self._queued_commands = commands self._task_queue.set_run_func(func=self._add_command_and_execute) @@ -358,9 +357,10 @@ async def run( # noqa: D102 return RunResult(commands=commands, state_summary=run_data, parameters=[]) async def _add_command_and_execute(self) -> None: + print(self._queued_commands) for command in self._queued_commands: result = await self._protocol_engine.add_and_execute_command(command) - if result.error: + if result and result.error: raise ProtocolCommandFailedError( original_error=result.error, message=f"{result.error.errorType}: {result.error.detail}", diff --git a/api/tests/opentrons/protocol_runner/test_protocol_runner.py b/api/tests/opentrons/protocol_runner/test_protocol_runner.py index 33ccd92a1ad..0be303ede3c 100644 --- a/api/tests/opentrons/protocol_runner/test_protocol_runner.py +++ b/api/tests/opentrons/protocol_runner/test_protocol_runner.py @@ -18,7 +18,12 @@ from opentrons.util.broker import Broker from opentrons import protocol_reader -from opentrons.protocol_engine import ProtocolEngine, Liquid, commands as pe_commands +from opentrons.protocol_engine import ( + ProtocolEngine, + Liquid, + commands as pe_commands, + errors as pe_errors, +) from opentrons.protocol_reader import ( ProtocolSource, JsonProtocolConfig, @@ -328,6 +333,32 @@ async def test_run_json_runner( ) +async def test_run_json_runner_stop_requested_breaks_loop( + decoy: Decoy, + hardware_api: HardwareAPI, + protocol_engine: ProtocolEngine, + task_queue: TaskQueue, + json_runner_subject: JsonRunner, +) -> None: + """It should run a protocol to completion.""" + json_runner_subject._queued_commands = [ + pe_commands.HomeCreate(params=pe_commands.HomeParams()) + ] + decoy.when(protocol_engine.state_view.commands.has_been_played()).then_return( + False, True + ) + + assert json_runner_subject.was_started() is False + await json_runner_subject.run(deck_configuration=[]) + assert json_runner_subject.was_started() is True + + decoy.when( + protocol_engine.add_and_execute_command( + pe_commands.HomeCreate(params=pe_commands.HomeParams()) + ) + ).then_raise(pe_errors.RunStoppedError("run stopped")) + + @pytest.mark.parametrize( "schema_version, json_protocol", [ @@ -385,6 +416,8 @@ async def test_load_json_runner( await json_runner_subject.load(json_protocol_source) + run_func_captor = matchers.Captor() + decoy.verify( protocol_engine.add_labware_definition(labware_definition), protocol_engine.add_liquid( @@ -393,7 +426,20 @@ async def test_load_json_runner( protocol_engine.add_command( request=pe_commands.HomeCreate(params=pe_commands.HomeParams(axes=None)) ), - task_queue.set_run_func(func=json_runner_subject._add_command_and_execute), + task_queue.set_run_func(func=run_func_captor), + ) + + print(run_func_captor.value) + + # assert run_func_captor.value is json_runner_subject._add_command_and_execute + + # Verify that the run func calls the right things: + run_func = run_func_captor.value + await run_func() + decoy.verify( + await protocol_engine.add_and_execute_command( + request=pe_commands.HomeCreate(params=pe_commands.HomeParams(axes=None)) + ) ) From 2e1c4f48a93b32101fce764b9d6177dac84b8912 Mon Sep 17 00:00:00 2001 From: tamarzanzouri Date: Mon, 1 Apr 2024 22:11:35 -0400 Subject: [PATCH 12/21] added captors and tested add_and_execute_command --- .../protocol_runner/test_protocol_runner.py | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/api/tests/opentrons/protocol_runner/test_protocol_runner.py b/api/tests/opentrons/protocol_runner/test_protocol_runner.py index 0be303ede3c..89b9e9899b5 100644 --- a/api/tests/opentrons/protocol_runner/test_protocol_runner.py +++ b/api/tests/opentrons/protocol_runner/test_protocol_runner.py @@ -438,8 +438,22 @@ async def test_load_json_runner( await run_func() decoy.verify( await protocol_engine.add_and_execute_command( - request=pe_commands.HomeCreate(params=pe_commands.HomeParams(axes=None)) - ) + request=pe_commands.WaitForResumeCreate( + params=pe_commands.WaitForResumeParams(message="hello") + ), + ), + await protocol_engine.add_and_execute_command( + request=pe_commands.WaitForResumeCreate( + params=pe_commands.WaitForResumeParams(message="goodbye") + ), + ), + await protocol_engine.add_and_execute_command( + request=pe_commands.LoadLiquidCreate( + params=pe_commands.LoadLiquidParams( + liquidId="water-id", labwareId="labware-id", volumeByWell={"A1": 30} + ) + ), + ), ) From cb83ccfbd33c13c2e7ad4b481fa5405aca995f00 Mon Sep 17 00:00:00 2001 From: tamarzanzouri Date: Mon, 1 Apr 2024 22:26:57 -0400 Subject: [PATCH 13/21] added test for break on stop --- .../protocol_runner/test_protocol_runner.py | 42 +++++++++++++------ 1 file changed, 30 insertions(+), 12 deletions(-) diff --git a/api/tests/opentrons/protocol_runner/test_protocol_runner.py b/api/tests/opentrons/protocol_runner/test_protocol_runner.py index 89b9e9899b5..737c2b3a2b5 100644 --- a/api/tests/opentrons/protocol_runner/test_protocol_runner.py +++ b/api/tests/opentrons/protocol_runner/test_protocol_runner.py @@ -339,21 +339,43 @@ async def test_run_json_runner_stop_requested_breaks_loop( protocol_engine: ProtocolEngine, task_queue: TaskQueue, json_runner_subject: JsonRunner, + json_file_reader: JsonFileReader, + json_translator: JsonTranslator, ) -> None: """It should run a protocol to completion.""" - json_runner_subject._queued_commands = [ + labware_definition = LabwareDefinition.construct() + json_protocol_source = ProtocolSource( + directory=Path("/dev/null"), + main_file=Path("/dev/null/abc.json"), + files=[], + metadata={}, + robot_type="OT-2 Standard", + config=JsonProtocolConfig(schema_version=6), + content_hash="abc123", + ) + + commands: List[pe_commands.CommandCreate] = [ pe_commands.HomeCreate(params=pe_commands.HomeParams()) ] - decoy.when(protocol_engine.state_view.commands.has_been_played()).then_return( - False, True - ) - assert json_runner_subject.was_started() is False - await json_runner_subject.run(deck_configuration=[]) - assert json_runner_subject.was_started() is True + json_protocol = ProtocolSchemaV6.construct() # type: ignore[call-arg] decoy.when( - protocol_engine.add_and_execute_command( + await protocol_reader.extract_labware_definitions(json_protocol_source) + ).then_return([labware_definition]) + decoy.when(json_file_reader.read(json_protocol_source)).then_return(json_protocol) + decoy.when(json_translator.translate_commands(json_protocol)).then_return(commands) + + await json_runner_subject.load(json_protocol_source) + + run_func_captor = matchers.Captor() + + # Verify that the run func calls the right things: + run_func = run_func_captor.value + await run_func() + + decoy.when( + await protocol_engine.add_and_execute_command( pe_commands.HomeCreate(params=pe_commands.HomeParams()) ) ).then_raise(pe_errors.RunStoppedError("run stopped")) @@ -429,10 +451,6 @@ async def test_load_json_runner( task_queue.set_run_func(func=run_func_captor), ) - print(run_func_captor.value) - - # assert run_func_captor.value is json_runner_subject._add_command_and_execute - # Verify that the run func calls the right things: run_func = run_func_captor.value await run_func() From 819d957671410e579f11adf5a0a587600613f7cf Mon Sep 17 00:00:00 2001 From: tamarzanzouri Date: Wed, 3 Apr 2024 17:41:13 -0400 Subject: [PATCH 14/21] added test to not execute commands if run stopped --- .../protocol_runner/test_protocol_runner.py | 53 ++++++++++++++++--- 1 file changed, 45 insertions(+), 8 deletions(-) diff --git a/api/tests/opentrons/protocol_runner/test_protocol_runner.py b/api/tests/opentrons/protocol_runner/test_protocol_runner.py index 737c2b3a2b5..12b0445045e 100644 --- a/api/tests/opentrons/protocol_runner/test_protocol_runner.py +++ b/api/tests/opentrons/protocol_runner/test_protocol_runner.py @@ -22,7 +22,6 @@ ProtocolEngine, Liquid, commands as pe_commands, - errors as pe_errors, ) from opentrons.protocol_reader import ( ProtocolSource, @@ -333,7 +332,7 @@ async def test_run_json_runner( ) -async def test_run_json_runner_stop_requested_breaks_loop( +async def test_run_json_runner_stop_requested_stops_enquqing( decoy: Decoy, hardware_api: HardwareAPI, protocol_engine: ProtocolEngine, @@ -343,7 +342,7 @@ async def test_run_json_runner_stop_requested_breaks_loop( json_translator: JsonTranslator, ) -> None: """It should run a protocol to completion.""" - labware_definition = LabwareDefinition.construct() + labware_definition = LabwareDefinition.construct() # type: ignore[call-arg] json_protocol_source = ProtocolSource( directory=Path("/dev/null"), main_file=Path("/dev/null/abc.json"), @@ -355,7 +354,19 @@ async def test_run_json_runner_stop_requested_breaks_loop( ) commands: List[pe_commands.CommandCreate] = [ - pe_commands.HomeCreate(params=pe_commands.HomeParams()) + pe_commands.HomeCreate(params=pe_commands.HomeParams()), + pe_commands.WaitForDurationCreate( + params=pe_commands.WaitForDurationParams(seconds=10) + ), + pe_commands.LoadLiquidCreate( + params=pe_commands.LoadLiquidParams( + liquidId="water-id", labwareId="labware-id", volumeByWell={"A1": 30} + ) + ), + ] + + liquids: List[Liquid] = [ + Liquid(id="water-id", displayName="water", description="water desc") ] json_protocol = ProtocolSchemaV6.construct() # type: ignore[call-arg] @@ -365,20 +376,46 @@ async def test_run_json_runner_stop_requested_breaks_loop( ).then_return([labware_definition]) decoy.when(json_file_reader.read(json_protocol_source)).then_return(json_protocol) decoy.when(json_translator.translate_commands(json_protocol)).then_return(commands) + decoy.when(json_translator.translate_liquids(json_protocol)).then_return(liquids) + decoy.when( + await protocol_engine.add_and_execute_command( + pe_commands.WaitForDurationCreate( + params=pe_commands.WaitForDurationParams(seconds=10) + ), + ) + ).then_return( + pe_commands.WaitForDuration.construct(status=pe_commands.CommandStatus.QUEUED) # type: ignore[call-arg] + ) await json_runner_subject.load(json_protocol_source) run_func_captor = matchers.Captor() + decoy.verify( + protocol_engine.add_labware_definition(labware_definition), + protocol_engine.add_liquid( + id="water-id", name="water", description="water desc", color=None + ), + protocol_engine.add_command( + request=pe_commands.HomeCreate(params=pe_commands.HomeParams(axes=None)) + ), + task_queue.set_run_func(func=run_func_captor), + ) + # Verify that the run func calls the right things: run_func = run_func_captor.value await run_func() - decoy.when( + decoy.verify( await protocol_engine.add_and_execute_command( - pe_commands.HomeCreate(params=pe_commands.HomeParams()) - ) - ).then_raise(pe_errors.RunStoppedError("run stopped")) + pe_commands.LoadLiquidCreate( + params=pe_commands.LoadLiquidParams( + liquidId="water-id", labwareId="labware-id", volumeByWell={"A1": 30} + ) + ) + ), + times=0, + ) @pytest.mark.parametrize( From c5249d3f1dcf0b37bfded9afba1356de7a3205b9 Mon Sep 17 00:00:00 2001 From: tamarzanzouri Date: Wed, 3 Apr 2024 18:33:37 -0400 Subject: [PATCH 15/21] fixed logic with test --- api/src/opentrons/protocol_runner/protocol_runner.py | 2 ++ .../opentrons/protocol_runner/test_protocol_runner.py | 7 +++++++ 2 files changed, 9 insertions(+) diff --git a/api/src/opentrons/protocol_runner/protocol_runner.py b/api/src/opentrons/protocol_runner/protocol_runner.py index 6e8a10e26a1..63108740db5 100644 --- a/api/src/opentrons/protocol_runner/protocol_runner.py +++ b/api/src/opentrons/protocol_runner/protocol_runner.py @@ -365,6 +365,8 @@ async def _add_command_and_execute(self) -> None: original_error=result.error, message=f"{result.error.errorType}: {result.error.detail}", ) + elif result.status == pe_commands.CommandStatus.QUEUED: + break class LiveRunner(AbstractRunner): diff --git a/api/tests/opentrons/protocol_runner/test_protocol_runner.py b/api/tests/opentrons/protocol_runner/test_protocol_runner.py index 12b0445045e..f7fc998d00f 100644 --- a/api/tests/opentrons/protocol_runner/test_protocol_runner.py +++ b/api/tests/opentrons/protocol_runner/test_protocol_runner.py @@ -377,6 +377,13 @@ async def test_run_json_runner_stop_requested_stops_enquqing( decoy.when(json_file_reader.read(json_protocol_source)).then_return(json_protocol) decoy.when(json_translator.translate_commands(json_protocol)).then_return(commands) decoy.when(json_translator.translate_liquids(json_protocol)).then_return(liquids) + decoy.when( + await protocol_engine.add_and_execute_command( + pe_commands.HomeCreate(params=pe_commands.HomeParams()), + ) + ).then_return( + pe_commands.Home.construct(status=pe_commands.CommandStatus.SUCCEEDED) # type: ignore[call-arg] + ) decoy.when( await protocol_engine.add_and_execute_command( pe_commands.WaitForDurationCreate( From 6c812d6dd454a9226ffe32226ba855a07a4e5df5 Mon Sep 17 00:00:00 2001 From: tamarzanzouri Date: Wed, 3 Apr 2024 21:59:57 -0400 Subject: [PATCH 16/21] added tests for papi and json play and stop in the middle of a run --- .../runs/test_play_stop_papi.tavern.yaml | 114 ++++++++++++++++++ .../runs/test_play_stop_v6.tavern.yaml | 114 ++++++++++++++++++ .../protocols/wait_for_resume_stop_papi.py | 13 ++ .../protocols/wait_for_resume_stop_v6.json | 36 ++++++ 4 files changed, 277 insertions(+) create mode 100644 robot-server/tests/integration/http_api/runs/test_play_stop_papi.tavern.yaml create mode 100644 robot-server/tests/integration/http_api/runs/test_play_stop_v6.tavern.yaml create mode 100644 robot-server/tests/integration/protocols/wait_for_resume_stop_papi.py create mode 100644 robot-server/tests/integration/protocols/wait_for_resume_stop_v6.json diff --git a/robot-server/tests/integration/http_api/runs/test_play_stop_papi.tavern.yaml b/robot-server/tests/integration/http_api/runs/test_play_stop_papi.tavern.yaml new file mode 100644 index 00000000000..ec1e97b1bd4 --- /dev/null +++ b/robot-server/tests/integration/http_api/runs/test_play_stop_papi.tavern.yaml @@ -0,0 +1,114 @@ +test_name: Test python protocol run commands are failed when stopped. + +marks: + - usefixtures: + - ot2_server_base_url +stages: + - name: Upload a python protocol + request: + url: '{ot2_server_base_url}/protocols' + method: POST + files: + files: 'tests/integration/protocols/wait_for_resume_stop_papi.py' + 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 + + - name: Stop the run + request: + url: '{ot2_server_base_url}/runs/{run_id}/actions' + method: POST + json: + data: + actionType: stop + response: + status_code: 201 + + - name: Wait for the run to complete + max_retries: 10 + delay_after: 0.2 + request: + url: '{ot2_server_base_url}/runs/{run_id}' + method: GET + response: + status_code: 200 + strict: + - json:off + json: + data: + status: stopped + + - name: Get run commands + max_retries: 10 + delay_after: 0.2 + request: + url: '{ot2_server_base_url}/runs/{run_id}/commands' + method: GET + response: + status_code: 200 + strict: + - json:off + json: + data: + - id: !anystr + key: !anystr + commandType: home + createdAt: !anystr + startedAt: !anystr + completedAt: !anystr + status: succeeded + params: {} + notes: [] + - id: !anystr + key: !anystr + commandType: home + createdAt: !anystr + startedAt: !anystr + completedAt: !anystr + status: succeeded + params: { } + notes: [ ] + - id: !anystr + key: !anystr + commandType: waitForResume + createdAt: !anystr + startedAt: !anystr + completedAt: !anystr + status: failed + params: { } + notes: [ ] + error: + createdAt: !anystr + detail: 'Run was cancelled' + errorCode: '4000' + errorInfo: { } + errorType: 'RunStoppedError' + id: !anystr + wrappedErrors: [ ] + + diff --git a/robot-server/tests/integration/http_api/runs/test_play_stop_v6.tavern.yaml b/robot-server/tests/integration/http_api/runs/test_play_stop_v6.tavern.yaml new file mode 100644 index 00000000000..73ce4c02721 --- /dev/null +++ b/robot-server/tests/integration/http_api/runs/test_play_stop_v6.tavern.yaml @@ -0,0 +1,114 @@ +test_name: Test a JSONv6 run can be paused and then cancelled. + +marks: + - usefixtures: + - ot2_server_base_url +stages: + - name: Upload a JSONv6 protocol + request: + url: '{ot2_server_base_url}/protocols' + method: POST + files: + files: 'tests/integration/protocols/wait_for_resume_stop_v6.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 + + - name: Stop the run + request: + url: '{ot2_server_base_url}/runs/{run_id}/actions' + method: POST + json: + data: + actionType: stop + response: + status_code: 201 + + - name: Wait for the run to complete + max_retries: 10 + delay_after: 0.2 + request: + url: '{ot2_server_base_url}/runs/{run_id}' + method: GET + response: + status_code: 200 + strict: + - json:off + json: + data: + status: stopped + + - name: Get run commands + max_retries: 10 + delay_after: 0.2 + request: + url: '{ot2_server_base_url}/runs/{run_id}/commands' + method: GET + response: + status_code: 200 + strict: + - json:off + json: + data: + - id: !anystr + key: !anystr + commandType: home + createdAt: !anystr + startedAt: !anystr + completedAt: !anystr + status: succeeded + params: {} + notes: [] + - id: !anystr + key: !anystr + commandType: home + createdAt: !anystr + startedAt: !anystr + completedAt: !anystr + status: succeeded + params: { } + notes: [ ] + - id: !anystr + key: !anystr + commandType: waitForResume + createdAt: !anystr + startedAt: !anystr + completedAt: !anystr + status: failed + params: { } + notes: [ ] + error: + createdAt: !anystr + detail: 'Run was cancelled' + errorCode: '4000' + errorInfo: { } + errorType: 'RunStoppedError' + id: !anystr + wrappedErrors: [ ] + + diff --git a/robot-server/tests/integration/protocols/wait_for_resume_stop_papi.py b/robot-server/tests/integration/protocols/wait_for_resume_stop_papi.py new file mode 100644 index 00000000000..8ef7bc4cd1c --- /dev/null +++ b/robot-server/tests/integration/protocols/wait_for_resume_stop_papi.py @@ -0,0 +1,13 @@ +from opentrons.protocol_api import ProtocolContext + +metadata = { + "protocolName": "stop while waiting test", + "author": "Opentrons ", + "apiLevel": "2.15", +} + + +def run(ctx: ProtocolContext) -> None: + ctx.home() + ctx.pause() + ctx.set_rail_lights(on=True) diff --git a/robot-server/tests/integration/protocols/wait_for_resume_stop_v6.json b/robot-server/tests/integration/protocols/wait_for_resume_stop_v6.json new file mode 100644 index 00000000000..5598f2f5779 --- /dev/null +++ b/robot-server/tests/integration/protocols/wait_for_resume_stop_v6.json @@ -0,0 +1,36 @@ +{ + "$otSharedSchema": "#/protocol/schemas/6", + "schemaVersion": 6, + "metadata": { + "protocolName": "Simple test protocol", + "author": "engineering ", + "description": "A short test protocol", + "created": 1223131231, + "tags": ["unitTest"] + }, + "robot": { + "model": "OT-2 Standard", + "deckId": "ot2_standard" + }, + "pipettes": {}, + "modules": {}, + "labware": {}, + "liquids": {}, + "labwareDefinitions": {}, + "commands": [ + { + "commandType": "home", + "params": {} + }, + { + "commandType": "waitForResume", + "params": { + } + }, + { + "commandType": "home", + "params": {} + } + ], + "commandAnnotations": [] +} From 76aa3e56aea59a40e99c4647e994d67163543379 Mon Sep 17 00:00:00 2001 From: tamarzanzouri Date: Wed, 3 Apr 2024 22:13:04 -0400 Subject: [PATCH 17/21] fixed unit test to match logic in e2e test --- .../protocol_runner/protocol_runner.py | 3 --- .../protocol_runner/test_protocol_runner.py | 24 +++++++++---------- 2 files changed, 12 insertions(+), 15 deletions(-) diff --git a/api/src/opentrons/protocol_runner/protocol_runner.py b/api/src/opentrons/protocol_runner/protocol_runner.py index 63108740db5..a1e88969615 100644 --- a/api/src/opentrons/protocol_runner/protocol_runner.py +++ b/api/src/opentrons/protocol_runner/protocol_runner.py @@ -357,7 +357,6 @@ async def run( # noqa: D102 return RunResult(commands=commands, state_summary=run_data, parameters=[]) async def _add_command_and_execute(self) -> None: - print(self._queued_commands) for command in self._queued_commands: result = await self._protocol_engine.add_and_execute_command(command) if result and result.error: @@ -365,8 +364,6 @@ async def _add_command_and_execute(self) -> None: original_error=result.error, message=f"{result.error.errorType}: {result.error.detail}", ) - elif result.status == pe_commands.CommandStatus.QUEUED: - break class LiveRunner(AbstractRunner): diff --git a/api/tests/opentrons/protocol_runner/test_protocol_runner.py b/api/tests/opentrons/protocol_runner/test_protocol_runner.py index f7fc998d00f..5497e9e12ab 100644 --- a/api/tests/opentrons/protocol_runner/test_protocol_runner.py +++ b/api/tests/opentrons/protocol_runner/test_protocol_runner.py @@ -1,4 +1,6 @@ """Tests for the PythonAndLegacyRunner, JsonRunner & LiveRunner classes.""" +from datetime import datetime + import pytest from pytest_lazyfixture import lazy_fixture # type: ignore[import-untyped] from decoy import Decoy, matchers @@ -22,6 +24,7 @@ ProtocolEngine, Liquid, commands as pe_commands, + errors as pe_errors, ) from opentrons.protocol_reader import ( ProtocolSource, @@ -391,7 +394,13 @@ async def test_run_json_runner_stop_requested_stops_enquqing( ), ) ).then_return( - pe_commands.WaitForDuration.construct(status=pe_commands.CommandStatus.QUEUED) # type: ignore[call-arg] + pe_commands.WaitForDuration.construct( # type: ignore[call-arg] + error=pe_errors.ErrorOccurrence.from_failed( + id="some-id", + createdAt=datetime(year=2021, month=1, day=1), + error=pe_errors.ProtocolEngineError(), + ) + ) ) await json_runner_subject.load(json_protocol_source) @@ -411,18 +420,9 @@ async def test_run_json_runner_stop_requested_stops_enquqing( # Verify that the run func calls the right things: run_func = run_func_captor.value - await run_func() - decoy.verify( - await protocol_engine.add_and_execute_command( - pe_commands.LoadLiquidCreate( - params=pe_commands.LoadLiquidParams( - liquidId="water-id", labwareId="labware-id", volumeByWell={"A1": 30} - ) - ) - ), - times=0, - ) + with pytest.raises(pe_errors.ProtocolEngineError): + await run_func() @pytest.mark.parametrize( From d3b0cd31e3ddfd4f78c57bdcdec26bc94e8ca4c9 Mon Sep 17 00:00:00 2001 From: tamarzanzouri Date: Thu, 4 Apr 2024 11:44:04 -0400 Subject: [PATCH 18/21] changed tests to use delay and wait for a running state --- .../runs/test_play_stop_papi.tavern.yaml | 20 +++++++++++++++++-- .../runs/test_play_stop_v6.tavern.yaml | 20 +++++++++++++++++-- .../protocols/wait_for_resume_stop_papi.py | 2 +- .../protocols/wait_for_resume_stop_v6.json | 3 ++- 4 files changed, 39 insertions(+), 6 deletions(-) diff --git a/robot-server/tests/integration/http_api/runs/test_play_stop_papi.tavern.yaml b/robot-server/tests/integration/http_api/runs/test_play_stop_papi.tavern.yaml index ec1e97b1bd4..ad81b53b1c7 100644 --- a/robot-server/tests/integration/http_api/runs/test_play_stop_papi.tavern.yaml +++ b/robot-server/tests/integration/http_api/runs/test_play_stop_papi.tavern.yaml @@ -39,6 +39,21 @@ stages: response: status_code: 201 + - name: Wait for the run to complete + max_retries: 10 + delay_after: 0.2 + request: + url: '{ot2_server_base_url}/runs/{run_id}/commands' + method: GET + response: + status_code: 200 + strict: + - json:off + json: + data: + - commandType: waitForDuration + status: running + - name: Stop the run request: url: '{ot2_server_base_url}/runs/{run_id}/actions' @@ -95,12 +110,13 @@ stages: notes: [ ] - id: !anystr key: !anystr - commandType: waitForResume + commandType: waitForDuration createdAt: !anystr startedAt: !anystr completedAt: !anystr status: failed - params: { } + params: + seconds: 30 notes: [ ] error: createdAt: !anystr diff --git a/robot-server/tests/integration/http_api/runs/test_play_stop_v6.tavern.yaml b/robot-server/tests/integration/http_api/runs/test_play_stop_v6.tavern.yaml index 73ce4c02721..b3a0cddaabb 100644 --- a/robot-server/tests/integration/http_api/runs/test_play_stop_v6.tavern.yaml +++ b/robot-server/tests/integration/http_api/runs/test_play_stop_v6.tavern.yaml @@ -39,6 +39,21 @@ stages: response: status_code: 201 + - name: Wait for the command to run + max_retries: 10 + delay_after: 0.2 + request: + url: '{ot2_server_base_url}/runs/{run_id}/commands' + method: GET + response: + status_code: 200 + strict: + - json:off + json: + data: + - commandType: waitForDuration + status: running + - name: Stop the run request: url: '{ot2_server_base_url}/runs/{run_id}/actions' @@ -95,12 +110,13 @@ stages: notes: [ ] - id: !anystr key: !anystr - commandType: waitForResume + commandType: waitForDuration createdAt: !anystr startedAt: !anystr completedAt: !anystr status: failed - params: { } + params: + seconds: 30 notes: [ ] error: createdAt: !anystr diff --git a/robot-server/tests/integration/protocols/wait_for_resume_stop_papi.py b/robot-server/tests/integration/protocols/wait_for_resume_stop_papi.py index 8ef7bc4cd1c..227d65cd00b 100644 --- a/robot-server/tests/integration/protocols/wait_for_resume_stop_papi.py +++ b/robot-server/tests/integration/protocols/wait_for_resume_stop_papi.py @@ -9,5 +9,5 @@ def run(ctx: ProtocolContext) -> None: ctx.home() - ctx.pause() + ctx.delay(seconds=30) ctx.set_rail_lights(on=True) diff --git a/robot-server/tests/integration/protocols/wait_for_resume_stop_v6.json b/robot-server/tests/integration/protocols/wait_for_resume_stop_v6.json index 5598f2f5779..05101595ee7 100644 --- a/robot-server/tests/integration/protocols/wait_for_resume_stop_v6.json +++ b/robot-server/tests/integration/protocols/wait_for_resume_stop_v6.json @@ -23,8 +23,9 @@ "params": {} }, { - "commandType": "waitForResume", + "commandType": "waitForDuration", "params": { + "seconds": 30 } }, { From 25344bd3a4edb37504807c8722dcf12d271a5d89 Mon Sep 17 00:00:00 2001 From: TamarZanzouri Date: Thu, 4 Apr 2024 12:13:43 -0400 Subject: [PATCH 19/21] Update robot-server/tests/integration/http_api/runs/test_play_stop_papi.tavern.yaml Co-authored-by: Max Marrone --- .../integration/http_api/runs/test_play_stop_papi.tavern.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/robot-server/tests/integration/http_api/runs/test_play_stop_papi.tavern.yaml b/robot-server/tests/integration/http_api/runs/test_play_stop_papi.tavern.yaml index ad81b53b1c7..ca55f81b5d9 100644 --- a/robot-server/tests/integration/http_api/runs/test_play_stop_papi.tavern.yaml +++ b/robot-server/tests/integration/http_api/runs/test_play_stop_papi.tavern.yaml @@ -39,7 +39,7 @@ stages: response: status_code: 201 - - name: Wait for the run to complete + - name: Wait for the command to run max_retries: 10 delay_after: 0.2 request: From 02a6ead09ef18552e69f2c071a545666c7ff844b Mon Sep 17 00:00:00 2001 From: tamarzanzouri Date: Thu, 4 Apr 2024 13:01:21 -0400 Subject: [PATCH 20/21] removed retry on get run commands --- .../integration/http_api/runs/test_play_stop_papi.tavern.yaml | 2 -- 1 file changed, 2 deletions(-) diff --git a/robot-server/tests/integration/http_api/runs/test_play_stop_papi.tavern.yaml b/robot-server/tests/integration/http_api/runs/test_play_stop_papi.tavern.yaml index ca55f81b5d9..d59b533ca67 100644 --- a/robot-server/tests/integration/http_api/runs/test_play_stop_papi.tavern.yaml +++ b/robot-server/tests/integration/http_api/runs/test_play_stop_papi.tavern.yaml @@ -79,8 +79,6 @@ stages: status: stopped - name: Get run commands - max_retries: 10 - delay_after: 0.2 request: url: '{ot2_server_base_url}/runs/{run_id}/commands' method: GET From 123f313f7d4e7e28782ba90cfe85c74b5802c670 Mon Sep 17 00:00:00 2001 From: tamarzanzouri Date: Thu, 4 Apr 2024 13:02:05 -0400 Subject: [PATCH 21/21] removed retry --- .../integration/http_api/runs/test_play_stop_v6.tavern.yaml | 2 -- 1 file changed, 2 deletions(-) diff --git a/robot-server/tests/integration/http_api/runs/test_play_stop_v6.tavern.yaml b/robot-server/tests/integration/http_api/runs/test_play_stop_v6.tavern.yaml index b3a0cddaabb..e3d6d5b659f 100644 --- a/robot-server/tests/integration/http_api/runs/test_play_stop_v6.tavern.yaml +++ b/robot-server/tests/integration/http_api/runs/test_play_stop_v6.tavern.yaml @@ -79,8 +79,6 @@ stages: status: stopped - name: Get run commands - max_retries: 10 - delay_after: 0.2 request: url: '{ot2_server_base_url}/runs/{run_id}/commands' method: GET