Skip to content

Commit

Permalink
fixup: who knows
Browse files Browse the repository at this point in the history
  • Loading branch information
mcous committed Jun 22, 2021
1 parent 82b3583 commit b011d29
Show file tree
Hide file tree
Showing 50 changed files with 1,332 additions and 1,761 deletions.
17 changes: 8 additions & 9 deletions api/src/opentrons/protocol_engine/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@
protocol state and side-effects like robot movements.
"""

from .create_protocol_engine import create_protocol_engine
from .protocol_engine import ProtocolEngine
from .state import StateStore, StateView, LabwareData
from .execution import CommandHandlers
from .resources import ResourceProviders
from .errors import ProtocolEngineError
from .state import State, StateView, LabwareData
from .types import (
DeckLocation,
DeckSlotLocation,
Expand All @@ -19,16 +19,15 @@
)

__all__ = [
# main export
# main factory and interface exports
"create_protocol_engine",
"ProtocolEngine",
# error types
"ProtocolEngineError",
# state interfaces and models
"StateStore",
"State",
"StateView",
"LabwareData",
# command execution interfaces
"CommandHandlers",
# resource management interfaces
"ResourceProviders",
# type definitions and other value models
"DeckLocation",
"DeckSlotLocation",
Expand Down
3 changes: 2 additions & 1 deletion api/src/opentrons/protocol_engine/clients/transports.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
from abc import ABC, abstractmethod
from asyncio import AbstractEventLoop, run_coroutine_threadsafe

from ..protocol_engine import ProtocolEngine, ProtocolEngineError
from ..protocol_engine import ProtocolEngine
from ..errors import ProtocolEngineError
from ..state import StateView
from ..commands import CommandRequest, CommandResult

Expand Down
10 changes: 6 additions & 4 deletions api/src/opentrons/protocol_engine/commands/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@
and/or schema generation.
"""

from .command import CommandStatus, BaseCommand, BaseCommandRequest
from .command import AbstractCommandImpl, BaseCommand, BaseCommandRequest, CommandStatus
from .command_mapper import CommandMapper
from .command_unions import Command, CommandRequest, CommandResult


Expand Down Expand Up @@ -48,16 +49,17 @@


__all__ = [
# command factory
"CommandBuilder",
# command model factory
"CommandMapper",
# command type unions
"Command",
"CommandRequest",
"CommandResult",
# base interfaces
"CommandStatus",
"AbstractCommandImpl",
"BaseCommand",
"BaseCommandRequest",
"CommandStatus",
# load labware command models
"LoadLabware",
"LoadLabwareRequest",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,11 @@
"""Add labware command."""
from __future__ import annotations
from datetime import datetime
from pydantic import BaseModel, Field
from typing import Optional
from typing import Optional, Type
from typing_extensions import Literal

from opentrons.protocols.models import LabwareDefinition
from .command import (
AbstractCommandImpl,
BaseCommand,
BaseCommandRequest,
CommandHandlers,
CommandStatus,
)
from .command import AbstractCommandImpl, BaseCommand, BaseCommandRequest

AddLabwareDefinitionCommandType = Literal["addLabwareDefinition"]

Expand Down Expand Up @@ -40,64 +33,40 @@ class AddLabwareDefinitionResult(BaseModel):
)


class AddLabwareDefinitionImplProvider:
"""Implementation provider mixin."""

data: AddLabwareDefinitionData

def get_implementation(self) -> AddLabwareDefinitionImplementation:
"""Get the execution implementation of an AddLabwareDefinition."""
return AddLabwareDefinitionImplementation(self.data)


class AddLabwareDefinitionRequest(
BaseCommandRequest[AddLabwareDefinitionData],
AddLabwareDefinitionImplProvider,
class AddLabwareDefinitionImplementation(
AbstractCommandImpl[AddLabwareDefinitionData, AddLabwareDefinitionResult]
):
"""Add labware definition command creation request."""
"""Add labware command implementation."""

commandType: AddLabwareDefinitionCommandType = "addLabwareDefinition"
data: AddLabwareDefinitionData
async def execute(
self, data: AddLabwareDefinitionData
) -> AddLabwareDefinitionResult:
"""Execute the add labware definition request."""
return AddLabwareDefinitionResult(
loadName=data.definition.parameters.loadName,
namespace=data.definition.namespace,
version=data.definition.version,
)


class AddLabwareDefinition(
BaseCommand[AddLabwareDefinitionData, AddLabwareDefinitionResult],
AddLabwareDefinitionImplProvider,
BaseCommand[AddLabwareDefinitionData, AddLabwareDefinitionResult]
):
"""Add labware definition command resource."""

commandType: AddLabwareDefinitionCommandType = "addLabwareDefinition"
data: AddLabwareDefinitionData
result: Optional[AddLabwareDefinitionResult]

_ImplementationCls: Type[
AddLabwareDefinitionImplementation
] = AddLabwareDefinitionImplementation

class AddLabwareDefinitionImplementation(
AbstractCommandImpl[
AddLabwareDefinitionData,
AddLabwareDefinitionResult,
AddLabwareDefinition,
]
):
"""Add labware command implementation."""

def create_command(
self,
command_id: str,
created_at: datetime,
status: CommandStatus = CommandStatus.QUEUED,
) -> AddLabwareDefinition:
"""Create a new AddLabwareDefinition command resource."""
return AddLabwareDefinition(
id=command_id,
createdAt=created_at,
status=status,
data=self._data,
)
class AddLabwareDefinitionRequest(BaseCommandRequest[AddLabwareDefinitionData]):
"""Add labware definition command creation request."""

async def execute(self, handlers: CommandHandlers) -> AddLabwareDefinitionResult:
"""Execute the add labware definition request."""
return AddLabwareDefinitionResult(
loadName=self._data.definition.parameters.loadName,
namespace=self._data.definition.namespace,
version=self._data.definition.version,
)
commandType: AddLabwareDefinitionCommandType = "addLabwareDefinition"
data: AddLabwareDefinitionData

_CommandCls: Type[AddLabwareDefinition] = AddLabwareDefinition
74 changes: 21 additions & 53 deletions api/src/opentrons/protocol_engine/commands/aspirate.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,11 @@
"""Aspirate command request, result, and implementation models."""
from __future__ import annotations
from datetime import datetime
from typing import Optional
from typing import Optional, Type
from typing_extensions import Literal


from .pipetting_common import BaseLiquidHandlingData, BaseLiquidHandlingResult
from .command import (
AbstractCommandImpl,
BaseCommand,
BaseCommandRequest,
CommandHandlers,
CommandStatus,
)
from .command import AbstractCommandImpl, BaseCommand, BaseCommandRequest

AspirateCommandType = Literal["aspirate"]

Expand All @@ -29,61 +22,36 @@ class AspirateResult(BaseLiquidHandlingResult):
pass


class AspirateImplProvider:
"""Implementation provider mixin."""

data: AspirateData

def get_implementation(self) -> AspirateImplementation:
"""Get the execution implementation of an Aspirate."""
return AspirateImplementation(self.data)

class AspirateImplementation(AbstractCommandImpl[AspirateData, AspirateResult]):
"""Aspirate command implementation."""

class AspirateRequest(BaseCommandRequest[AspirateData], AspirateImplProvider):
"""Create aspirate command request model."""
async def execute(self, data: AspirateData) -> AspirateResult:
"""Move to and aspirate from the requested well."""
volume = await self._pipetting.aspirate(
pipette_id=data.pipetteId,
labware_id=data.labwareId,
well_name=data.wellName,
well_location=data.wellLocation,
volume=data.volume,
)

commandType: AspirateCommandType = "aspirate"
data: AspirateData
return AspirateResult(volume=volume)


class Aspirate(BaseCommand[AspirateData, AspirateResult], AspirateImplProvider):
class Aspirate(BaseCommand[AspirateData, AspirateResult]):
"""Aspirate command model."""

commandType: AspirateCommandType = "aspirate"
data: AspirateData
result: Optional[AspirateResult]

_ImplementationCls: Type[AspirateImplementation] = AspirateImplementation

class AspirateImplementation(
AbstractCommandImpl[AspirateData, AspirateResult, Aspirate]
):
"""Aspirate command implementation."""

def create_command(
self,
command_id: str,
created_at: datetime,
status: CommandStatus = CommandStatus.QUEUED,
) -> Aspirate:
"""Create a new Aspirate command resource."""
return Aspirate(
id=command_id,
createdAt=created_at,
status=status,
data=self._data,
)
class AspirateRequest(BaseCommandRequest[AspirateData]):
"""Create aspirate command request model."""

async def execute(
self,
handlers: CommandHandlers,
) -> AspirateResult:
"""Move to and aspirate from the requested well."""
volume = await handlers.pipetting.aspirate(
pipette_id=self._data.pipetteId,
labware_id=self._data.labwareId,
well_name=self._data.wellName,
well_location=self._data.wellLocation,
volume=self._data.volume,
)
commandType: AspirateCommandType = "aspirate"
data: AspirateData

return AspirateResult(volume=volume)
_CommandCls: Type[Aspirate] = Aspirate
35 changes: 13 additions & 22 deletions api/src/opentrons/protocol_engine/commands/command.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,7 @@

# convenience type alias to work around type-only circular dependency
if TYPE_CHECKING:
from ..execution import CommandHandlers
else:
CommandHandlers = None

from ..execution import EquipmentHandler, MovementHandler, PipettingHandler

CommandDataT = TypeVar("CommandDataT", bound=BaseModel)

Expand Down Expand Up @@ -87,7 +84,7 @@ class BaseCommand(GenericModel, Generic[CommandDataT, CommandResultT]):

class AbstractCommandImpl(
ABC,
Generic[CommandDataT, CommandResultT, CommandT],
Generic[CommandDataT, CommandResultT],
):
"""Abstract command creation and execution implementation.
Expand All @@ -98,24 +95,18 @@ class AbstractCommandImpl(
- Execute the command, mapping data from execution into the result model
"""

_data: CommandDataT

# TODO(mc, 2021-06-21): `__init__` and `execute` args are pretty clearly
# swapped. `data` should be fed into execute, while `handlers` should be
# fed into `__init__`
def __init__(self, data: CommandDataT) -> None:
"""Initialize a command implementation from a command request."""
self._data = data

# TODO(mc, 2021-06-21): this method is awkward and not well-suited for this
# interface. Find a less verbose way of creating a full command resource
# from the request instance
@abstractmethod
def create_command(self, command_id: str, created_at: datetime) -> CommandT:
"""Create a new command resource from the implementation's request."""
...
def __init__(
self,
equipment: EquipmentHandler,
movement: MovementHandler,
pipetting: PipettingHandler,
) -> None:
"""Initialize the command implementation with execution handlers."""
self._equipment = equipment
self._movement = movement
self._pipetting = pipetting

@abstractmethod
async def execute(self, handlers: CommandHandlers) -> CommandResultT:
async def execute(self, data: CommandDataT) -> CommandResultT:
"""Execute the command, mapping data from execution into a response model."""
...
28 changes: 27 additions & 1 deletion api/src/opentrons/protocol_engine/commands/command_mapper.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,31 @@
"""Map command request and model types to other values."""
from datetime import datetime
from typing import Any

from .command import CommandStatus
from .command_unions import Command, CommandRequest


class CommandMapper:
pass
"""Static methods to map and modify command models."""

@staticmethod
def map_request_to_command(
request: CommandRequest,
command_id: str,
created_at: datetime,
) -> Command:
"""Map a CommandRequest instance to a full command."""
# NOTE(mc, 2021-06-22): mypy has trouble with this automatic
# request > command routing, but behavior is covered by unit tests
return request._CommandCls(
id=command_id,
createdAt=created_at,
status=CommandStatus.QUEUED,
data=request.data, # type: ignore[arg-type]
)

@staticmethod
def update_command(command: Command, **update: Any) -> Command:
"""Map a Command to a new Command instance with a different status."""
return command.copy(update=update)
Loading

0 comments on commit b011d29

Please sign in to comment.