Skip to content

Commit

Permalink
merge in PLAT-474-add-multi-readings branch
Browse files Browse the repository at this point in the history
  • Loading branch information
vegano1 committed Sep 18, 2024
1 parent e112fe7 commit 09341e4
Show file tree
Hide file tree
Showing 16 changed files with 101 additions and 49 deletions.
14 changes: 11 additions & 3 deletions api/src/opentrons/drivers/absorbance_reader/abstract.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from abc import ABC, abstractmethod
from typing import Dict, List, Tuple
from typing import Dict, List, Optional, Tuple
from opentrons.drivers.types import (
ABSMeasurementMode,
AbsorbanceReaderLidStatus,
AbsorbanceReaderDeviceState,
AbsorbanceReaderPlatePresence,
Expand Down Expand Up @@ -32,11 +33,18 @@ async def get_available_wavelengths(self) -> List[int]:
...

@abstractmethod
async def get_single_measurement(self, wavelength: int) -> List[float]:
async def initialize_measurement(
self,
wavelengths: List[int],
mode: ABSMeasurementMode = ABSMeasurementMode.SINGLE,
reference_wavelength: Optional[int] = None,
) -> None:
"""Initialize measurement for the device in single or multi mode for the given wavelengths"""
...

@abstractmethod
async def initialize_measurement(self, wavelength: int) -> None:
async def get_measurement(self) -> List[List[float]]:
"""Gets an absorbance reading with the current config."""
...

@abstractmethod
Expand Down
40 changes: 25 additions & 15 deletions api/src/opentrons/drivers/absorbance_reader/async_byonoy.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,13 @@
ErrorCodeNames,
DeviceStateNames,
SlotStateNames,
MeasurementConfig,
)
from opentrons.drivers.types import (
AbsorbanceReaderLidStatus,
AbsorbanceReaderPlatePresence,
AbsorbanceReaderDeviceState,
ABSMeasurementMode,
)
from opentrons.drivers.rpi_drivers.types import USBPort
from opentrons.hardware_control.modules.errors import AbsorbanceReaderDisconnectedError
Expand Down Expand Up @@ -110,7 +112,7 @@ def __init__(
self._loop = loop
self._supported_wavelengths: Optional[list[int]] = None
self._device_handle: Optional[int] = None
self._current_config: Optional[AbsProtocol.MeasurementConfig] = None
self._current_config: Optional[MeasurementConfig] = None

async def open(self) -> bool:
"""
Expand Down Expand Up @@ -225,8 +227,8 @@ async def get_supported_wavelengths(self) -> list[int]:
self._supported_wavelengths = wavelengths
return wavelengths

async def get_single_measurement(self, wavelength: int) -> List[float]:
"""Get a single measurement based on the current configuration."""
async def get_measurement(self) -> List[List[float]]:
"""Get a measurement based on the current configuration."""
handle = self._verify_device_handle()
assert (
self._current_config is not None
Expand All @@ -237,13 +239,13 @@ async def get_single_measurement(self, wavelength: int) -> List[float]:
err, measurements = await self._loop.run_in_executor(
executor=self._executor,
func=partial(
self._interface.byonoy_abs96_single_measure,
measure_func,
handle,
self._current_config,
),
)
self._raise_if_error(err.name, f"Error getting single measurement: {err}")
return measurements
self._raise_if_error(err.name, f"Error getting measurement: {err}")
return measurements if isinstance(measurements[0], List) else [measurements] # type: ignore

async def get_plate_presence(self) -> AbsorbanceReaderPlatePresence:
"""Get the state of the plate for the reader."""
Expand All @@ -265,7 +267,7 @@ def _get_supported_wavelengths(self) -> List[int]:
self._supported_wavelengths = wavelengths
return wavelengths

def _initialize_measurement(self, conf: AbsProtocol.MeasurementConfig) -> None:
def _initialize_measurement(self, conf: MeasurementConfig) -> None:
handle = self._verify_device_handle()
if isinstance(conf, AbsProtocol.SingleMeasurementConfig):
err = self._interface.byonoy_abs96_initialize_single_measurement(
Expand All @@ -278,7 +280,12 @@ def _initialize_measurement(self, conf: AbsProtocol.MeasurementConfig) -> None:
self._raise_if_error(err.name, f"Error initializing measurement: {err}")
self._current_config = conf

def _set_sample_wavelength(self, wavelength: int) -> AbsProtocol.MeasurementConfig:
def _initialize(
self,
mode: ABSMeasurementMode,
wavelengths: List[int],
reference_wavelength: Optional[int] = None,
) -> None:
if not self._supported_wavelengths:
self._get_supported_wavelengths()
assert self._supported_wavelengths
Expand All @@ -293,17 +300,20 @@ def _set_sample_wavelength(self, wavelength: int) -> AbsProtocol.MeasurementConf
conf.sample_wavelengths = wavelengths
else:
raise ValueError(
f"Unsupported wavelength: {wavelength}, expected: {self._supported_wavelengths}"
f"Unsupported wavelength: {wavelengths}, expected: {self._supported_wavelengths}"
)

def _initialize(self, wavelength: int) -> None:
conf = self._set_sample_wavelength(wavelength)
self._initialize_measurement(conf)

async def initialize(self, wavelength: int) -> None:
"""Initialize the device so we can start reading samples from it."""
async def initialize(
self,
mode: ABSMeasurementMode,
wavelengths: List[int],
reference_wavelength: Optional[int] = None,
) -> None:
"""initialize the device so we can start reading samples from it."""
await self._loop.run_in_executor(
executor=self._executor, func=partial(self._initialize, wavelength)
executor=self._executor,
func=partial(self._initialize, mode, wavelengths, reference_wavelength),
)

def _verify_device_handle(self) -> int:
Expand Down
5 changes: 3 additions & 2 deletions api/src/opentrons/drivers/absorbance_reader/driver.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from typing import Dict, Optional, List, Tuple, TYPE_CHECKING

from opentrons.drivers.types import (
ABSMeasurementMode,
AbsorbanceReaderLidStatus,
AbsorbanceReaderDeviceState,
AbsorbanceReaderPlatePresence,
Expand Down Expand Up @@ -70,8 +71,8 @@ async def initialize_measurement(
) -> None:
await self._connection.initialize(mode, wavelengths, reference_wavelength)

async def initialize_measurement(self, wavelength: int) -> None:
await self._connection.initialize(wavelength)
async def get_measurement(self) -> List[List[float]]:
return await self._connection.get_measurement()

async def get_status(self) -> AbsorbanceReaderDeviceState:
return await self._connection.get_device_status()
Expand Down
17 changes: 15 additions & 2 deletions api/src/opentrons/drivers/absorbance_reader/hid_protocol.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
from typing import (
Dict,
Optional,
Protocol,
List,
Literal,
Tuple,
Union,
runtime_checkable,
TypeVar,
)
Expand Down Expand Up @@ -69,8 +71,13 @@ class SlotState(Protocol):
value: int

@runtime_checkable
class MeasurementConfig(Protocol):
class SingleMeasurementConfig(Protocol):
sample_wavelength: int
reference_wavelength: Optional[int]

@runtime_checkable
class MultiMeasurementConfig(Protocol):
sample_wavelengths: List[int]

@runtime_checkable
class DeviceInfo(Protocol):
Expand Down Expand Up @@ -143,7 +150,7 @@ def byonoy_abs96_initialize_multiple_measurement(
...

def byonoy_abs96_single_measure(
self, device_handle: int, conf: MeasurementConfig
self, device_handle: int, conf: SingleMeasurementConfig
) -> Tuple[ErrorCode, List[float]]:
...

Expand All @@ -154,3 +161,9 @@ def byonoy_abs96_multiple_measure(

def byonoy_available_devices(self) -> List[Device]:
...


MeasurementConfig = Union[
AbsorbanceHidInterface.SingleMeasurementConfig,
AbsorbanceHidInterface.MultiMeasurementConfig,
]
12 changes: 9 additions & 3 deletions api/src/opentrons/drivers/absorbance_reader/simulator.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
from opentrons.util.async_helpers import ensure_yield

from opentrons.drivers.types import (
ABSMeasurementMode,
AbsorbanceReaderLidStatus,
AbsorbanceReaderDeviceState,
AbsorbanceReaderPlatePresence,
Expand Down Expand Up @@ -54,11 +55,16 @@ async def get_available_wavelengths(self) -> List[int]:
return [450, 570, 600, 650]

@ensure_yield
async def get_single_measurement(self, wavelength: int) -> List[float]:
return [0.0]
async def get_measurement(self) -> List[List[float]]:
return [[0.0]]

@ensure_yield
async def initialize_measurement(self, wavelength: int) -> None:
async def initialize_measurement(
self,
wavelengths: List[int],
mode: ABSMeasurementMode = ABSMeasurementMode.SINGLE,
reference_wavelength: Optional[int] = None,
) -> None:
pass

@ensure_yield
Expand Down
2 changes: 1 addition & 1 deletion api/src/opentrons/drivers/types.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
""" Type definitions for modules in this tree """
from dataclasses import dataclass
from typing import Dict, NamedTuple, Optional
from typing import Any, Dict, List, NamedTuple, Optional
from enum import Enum


Expand Down
30 changes: 21 additions & 9 deletions api/src/opentrons/hardware_control/modules/absorbance_reader.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
AbsorbanceReaderLidStatus,
AbsorbanceReaderPlatePresence,
AbsorbanceReaderDeviceState,
ABSMeasurementMode,
ABSMeasurementConfig,
)

from opentrons.hardware_control.execution_manager import ExecutionManager
Expand Down Expand Up @@ -194,13 +196,21 @@ def __init__(
self._device_info = device_info
self._reader = reader
self._poller = poller
self._measurement_config: Optional[ABSMeasurementConfig] = None
self._device_status = AbsorbanceReaderStatus.IDLE
self._error: Optional[str] = None
self._reader.register_error_handler(self._enter_error_state)

@property
def status(self) -> AbsorbanceReaderStatus:
"""Return some string describing status."""
return AbsorbanceReaderStatus.IDLE
"""Return some string describing the device status."""
state = self._reader.device_state
if state not in [
AbsorbanceReaderDeviceState.UNKNOWN,
AbsorbanceReaderDeviceState.OK,
]:
return AbsorbanceReaderStatus.ERROR
return self._device_status

@property
def lid_status(self) -> AbsorbanceReaderLidStatus:
Expand Down Expand Up @@ -236,6 +246,8 @@ def live_data(self) -> LiveData:
return {
"status": self.status.value,
"data": {
"uptime": self.uptime,
"deviceStatus": self.status.value,
"lidStatus": self.lid_status.value,
"platePresence": self.plate_presence.value,
"measureMode": conf.get("measureMode", ""),
Expand Down Expand Up @@ -345,13 +357,13 @@ async def set_sample_wavelength(
reference_wavelength=reference_wavelength,
)

async def start_measure(self, wavelength: int) -> List[float]:
"""Initiate a single measurement."""
return await self._driver.get_single_measurement(wavelength)

async def get_current_wavelength(self) -> None:
"""Get the Absorbance Reader's current active wavelength."""
pass # TODO: implement
async def start_measure(self) -> List[List[float]]:
"""Initiate a measurement depending on the measurement mode."""
try:
self._device_status = AbsorbanceReaderStatus.MEASURING
return await self._driver.get_measurement()
finally:
self._device_status = AbsorbanceReaderStatus.IDLE

async def get_current_lid_status(self) -> AbsorbanceReaderLidStatus:
"""Get the Absorbance Reader's current lid status."""
Expand Down
6 changes: 3 additions & 3 deletions api/src/opentrons/hardware_control/modules/magdeck.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import asyncio
import logging
from typing import Mapping, Optional
from typing import Dict, Optional
from opentrons.drivers.mag_deck import (
SimulatingDriver,
MagDeckDriver,
Expand Down Expand Up @@ -83,7 +83,7 @@ def __init__(
execution_manager: ExecutionManager,
hw_control_loop: asyncio.AbstractEventLoop,
driver: AbstractMagDeckDriver,
device_info: Mapping[str, str],
device_info: Dict[str, str],
disconnected_callback: types.ModuleDisconnectedCallback = None,
) -> None:
"""Constructor"""
Expand Down Expand Up @@ -166,7 +166,7 @@ def current_height(self) -> float:
return self._current_height

@property
def device_info(self) -> Mapping[str, str]:
def device_info(self) -> Dict[str, str]:
"""
Returns: a dict
Expand Down
2 changes: 1 addition & 1 deletion api/src/opentrons/hardware_control/modules/mod_abc.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ def parse_fw_version(version: str) -> Version:
raise InvalidVersion()
except InvalidVersion:
device_version = parse("v0.0.0")
return cast(Version, device_version) # type: ignore
return cast(Version, device_version)


class AbstractModule(abc.ABC):
Expand Down
8 changes: 4 additions & 4 deletions api/src/opentrons/hardware_control/modules/tempdeck.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import asyncio
import logging
from typing import Mapping, Optional
from typing import Dict, Optional

from opentrons.hardware_control.modules.types import (
ModuleDisconnectedCallback,
Expand All @@ -29,7 +29,7 @@
class TempDeck(mod_abc.AbstractModule):
"""Hardware control interface for an attached Temperature Module."""

MODULE_TYPE = types.ModuleType.TEMPERATURE
MODULE_TYPE = types.ModuleType["TEMPERATURE"]
FIRST_GEN2_REVISION = 20

@classmethod
Expand Down Expand Up @@ -100,7 +100,7 @@ def __init__(
driver: AbstractTempDeckDriver,
reader: TempDeckReader,
poller: Poller,
device_info: Mapping[str, str],
device_info: Dict[str, str],
hw_control_loop: asyncio.AbstractEventLoop,
disconnected_callback: ModuleDisconnectedCallback = None,
) -> None:
Expand Down Expand Up @@ -190,7 +190,7 @@ async def deactivate(self, must_be_running: bool = True) -> None:
await self._reader.read()

@property
def device_info(self) -> Mapping[str, str]:
def device_info(self) -> Dict[str, str]:
return self._device_info

@property
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
from opentrons.drivers.types import AbsorbanceReaderLidStatus

if TYPE_CHECKING:
from opentrons.protocol_engine.state import StateView
from opentrons.protocol_engine.state.state import StateView
from opentrons.protocol_engine.execution import (
EquipmentHandler,
LabwareMovementHandler,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
from ...errors.error_occurrence import ErrorOccurrence

if TYPE_CHECKING:
from opentrons.protocol_engine.state import StateView
from opentrons.protocol_engine.state.state import StateView
from opentrons.protocol_engine.execution import EquipmentHandler


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
from opentrons.drivers.types import AbsorbanceReaderLidStatus

if TYPE_CHECKING:
from opentrons.protocol_engine.state import StateView
from opentrons.protocol_engine.state.state import StateView
from opentrons.protocol_engine.execution import (
EquipmentHandler,
LabwareMovementHandler,
Expand Down
Loading

0 comments on commit 09341e4

Please sign in to comment.