From ced2175a1f7eb3537ed7717e1e19e048c139a9c3 Mon Sep 17 00:00:00 2001 From: ahiuchingau <20424172+ahiuchingau@users.noreply.github.com> Date: Tue, 30 Apr 2024 17:31:54 -0400 Subject: [PATCH] add absorbance reader command to protocol engine 1 --- .../commands/absorbance_reader/__init__.py | 1 + .../commands/absorbance_reader/run_profile.py | 68 +++++++++++++++++++ .../state/module_substates/__init__.py | 5 +- .../absorbance_reader_substate.py | 22 ++++++ 4 files changed, 95 insertions(+), 1 deletion(-) create mode 100644 api/src/opentrons/protocol_engine/commands/absorbance_reader/__init__.py create mode 100644 api/src/opentrons/protocol_engine/commands/absorbance_reader/run_profile.py create mode 100644 api/src/opentrons/protocol_engine/state/module_substates/absorbance_reader_substate.py diff --git a/api/src/opentrons/protocol_engine/commands/absorbance_reader/__init__.py b/api/src/opentrons/protocol_engine/commands/absorbance_reader/__init__.py new file mode 100644 index 000000000000..9a273d2a9be1 --- /dev/null +++ b/api/src/opentrons/protocol_engine/commands/absorbance_reader/__init__.py @@ -0,0 +1 @@ +"""Command models for Absorbance Reader commands.""" diff --git a/api/src/opentrons/protocol_engine/commands/absorbance_reader/run_profile.py b/api/src/opentrons/protocol_engine/commands/absorbance_reader/run_profile.py new file mode 100644 index 000000000000..467f1d7cac04 --- /dev/null +++ b/api/src/opentrons/protocol_engine/commands/absorbance_reader/run_profile.py @@ -0,0 +1,68 @@ +"""Command models to execute a Thermocycler profile.""" +from __future__ import annotations +from typing import List, Optional, TYPE_CHECKING +from typing_extensions import Literal, Type + +from pydantic import BaseModel, Field + +from opentrons.hardware_control.modules.types import ThermocyclerStep + +from ..command import AbstractCommandImpl, BaseCommand, BaseCommandCreate + +if TYPE_CHECKING: + from opentrons.protocol_engine.state import StateView + from opentrons.protocol_engine.execution import EquipmentHandler + + +RunProfileCommandType = Literal["thermocycler/runProfile"] + + +class AbsorbanceReadParams(BaseModel): + """Input parameters for a single absorbance reading.""" + + moduleId: str = Field(..., description="Unique ID of the Thermocycler.") + sampleWavelength: float = Field(..., description="Sample wavelength in nm.") + + +class AbsorbanceReadResult(BaseModel): + """Result data from running an aborbance reading.""" + + data: List[float] = Field(..., description="Absorbance data points.") + + +class AbsorbanceReadImpl(AbstractCommandImpl[AbsorbanceReadParams, AbsorbanceReadResult]): + """Execution implementation of a Thermocycler's run profile command.""" + + def __init__( + self, + state_view: StateView, + equipment: EquipmentHandler, + **unused_dependencies: object, + ) -> None: + self._state_view = state_view + self._equipment = equipment + + async def execute(self, params: AbsorbanceReadParams) -> AbsorbanceReadResult: + """Initiate a single absorbance measurement.""" + # TODO: Implement this + return AbsorbanceReadResult(data=[]) + + +class AbsorbanceRead(BaseCommand[AbsorbanceReadParams, AbsorbanceReadResult]): + """A command to execute a Thermocycler profile run.""" + + # TODO: fix this + commandType: AbsorbanceReadCommandType = "absorbanceReader/measure" + params: AbsorbanceReadParams + result: Optional[AbsorbanceReadResult] + + _ImplementationCls: Type[AbsorbanceReadImpl] = AbsorbanceReadImpl + + +class RunProfileCreate(BaseCommandCreate[AbsorbanceReadParams]): + """A request to execute a Thermocycler profile run.""" + + commandType: AbsorbanceReadCommandType = "absorbanceReader/measure" + params: AbsorbanceReadParams + + _CommandCls: Type[AbsorbanceRead] = AbsorbanceRead diff --git a/api/src/opentrons/protocol_engine/state/module_substates/__init__.py b/api/src/opentrons/protocol_engine/state/module_substates/__init__.py index 2e9b18ba17ba..3865982612be 100644 --- a/api/src/opentrons/protocol_engine/state/module_substates/__init__.py +++ b/api/src/opentrons/protocol_engine/state/module_substates/__init__.py @@ -12,7 +12,7 @@ ThermocyclerModuleId, ) from .magnetic_block_substate import MagneticBlockSubState, MagneticBlockId - +from .absorbance_reader_substate import AbsorbanceReaderSubState, AbsorbanceReaderId ModuleSubStateType = Union[ HeaterShakerModuleSubState, @@ -20,6 +20,7 @@ TemperatureModuleSubState, ThermocyclerModuleSubState, MagneticBlockSubState, + AbsorbanceReaderSubState, ] __all__ = [ @@ -33,6 +34,8 @@ "ThermocyclerModuleId", "MagneticBlockSubState", "MagneticBlockId", + "AbsorbanceReaderSubState", + "AbsorbanceReaderId", # Union of all module substates "ModuleSubStateType", ] diff --git a/api/src/opentrons/protocol_engine/state/module_substates/absorbance_reader_substate.py b/api/src/opentrons/protocol_engine/state/module_substates/absorbance_reader_substate.py new file mode 100644 index 000000000000..5b1cada15cc7 --- /dev/null +++ b/api/src/opentrons/protocol_engine/state/module_substates/absorbance_reader_substate.py @@ -0,0 +1,22 @@ +"""Heater-Shaker Module sub-state.""" +from dataclasses import dataclass +from typing import NewType, List + +from opentrons.protocol_engine.types import ( + TemperatureRange, + SpeedRange, + HeaterShakerLatchStatus, +) + +AbsorbanceReaderId = NewType("AbsorbanceReaderId", str) + + +@dataclass(frozen=True) +class AbsorbanceReaderSubState: + """Absorbance-Plate-Reader-specific state.""" + + module_id: AbsorbanceReaderId + is_lid_open: bool + is_loaded: bool + sample_wavelength: int + supported_wavelengths: List[int]