Skip to content

Commit

Permalink
refactor(api, robot-server, shared-data): replace v3 deck definition …
Browse files Browse the repository at this point in the history
…with v4 in protocol api and engine (#13854)

Switches V3 Deck to V4 in Protocol API and Engine, removes automatically loaded fixed trash from empty runs and maintenance runs, and introduces legacy fixed trash loading for older protocols.
  • Loading branch information
jbleon95 authored Oct 31, 2023
1 parent e23a8ea commit 8707cdc
Show file tree
Hide file tree
Showing 45 changed files with 690 additions and 328 deletions.
4 changes: 4 additions & 0 deletions api/src/opentrons/execute.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@
from opentrons.protocols import parse
from opentrons.protocols.api_support.deck_type import (
guess_from_global_config as guess_deck_type_from_global_config,
should_load_fixed_trash,
should_load_fixed_trash_for_python_protocol,
)
from opentrons.protocols.api_support.types import APIVersion
from opentrons.protocols.execution import execute as execute_apiv2
Expand Down Expand Up @@ -536,6 +538,7 @@ def _create_live_context_pe(
config=_get_protocol_engine_config(),
drop_tips_after_run=False,
post_run_hardware_state=PostRunHardwareState.STAY_ENGAGED_IN_PLACE,
load_fixed_trash=should_load_fixed_trash_for_python_protocol(api_version),
)
)

Expand Down Expand Up @@ -609,6 +612,7 @@ async def run(protocol_source: ProtocolSource) -> None:
protocol_engine = await create_protocol_engine(
hardware_api=hardware_api.wrapped(),
config=_get_protocol_engine_config(),
load_fixed_trash=should_load_fixed_trash(protocol_source.config),
)

protocol_runner = create_protocol_runner(
Expand Down
10 changes: 2 additions & 8 deletions api/src/opentrons/protocol_api/core/engine/instrument.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
from opentrons.protocol_engine.errors.exceptions import TipNotAttachedError
from opentrons.protocol_engine.clients import SyncClient as EngineClient
from opentrons.protocols.api_support.definitions import MAX_SUPPORTED_VERSION
from opentrons.types import Point
from opentrons.types import Point, DeckSlotName

from opentrons_shared_data.pipette.dev_types import PipetteNameType

Expand Down Expand Up @@ -409,13 +409,7 @@ def _move_to_waste_chute(
slot_origin_to_tip_a1 = _waste_chute_dimensions.SLOT_ORIGIN_TO_1_OR_8_TIP_A1

# TODO: All of this logic to compute the destination coordinate belongs in Protocol Engine.
slot_d3 = next(
s
for s in self._protocol_core.get_deck_definition()["locations"][
"orderedSlots"
]
if s["id"] == "D3"
)
slot_d3 = self._protocol_core.get_slot_definition(DeckSlotName.SLOT_D3)
slot_d3_origin = Point(*slot_d3["position"])
destination_point = slot_d3_origin + slot_origin_to_tip_a1

Expand Down
20 changes: 12 additions & 8 deletions api/src/opentrons/protocol_api/core/engine/protocol.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from opentrons.protocol_api import _waste_chute_dimensions

from opentrons.protocol_engine.commands import LoadModuleResult
from opentrons_shared_data.deck.dev_types import DeckDefinitionV3, SlotDefV3
from opentrons_shared_data.deck.dev_types import DeckDefinitionV4, SlotDefV3
from opentrons_shared_data.labware.labware_definition import LabwareDefinition
from opentrons_shared_data.labware.dev_types import LabwareDefinition as LabwareDefDict
from opentrons_shared_data.pipette.dev_types import PipetteNameType
Expand Down Expand Up @@ -102,17 +102,20 @@ def robot_type(self) -> RobotType:
return self._engine_client.state.config.robot_type

@property
def fixed_trash(self) -> LabwareCore:
def fixed_trash(self) -> Optional[LabwareCore]:
"""Get the fixed trash labware."""
trash_id = self._engine_client.state.labware.get_fixed_trash_id()
return self._labware_cores_by_id[trash_id]
if trash_id is not None:
return self._labware_cores_by_id[trash_id]
return None

def _load_fixed_trash(self) -> None:
trash_id = self._engine_client.state.labware.get_fixed_trash_id()
self._labware_cores_by_id[trash_id] = LabwareCore(
labware_id=trash_id,
engine_client=self._engine_client,
)
if trash_id is not None:
self._labware_cores_by_id[trash_id] = LabwareCore(
labware_id=trash_id,
engine_client=self._engine_client,
)

def get_max_speeds(self) -> AxisMaxSpeeds:
"""Get a control interface for maximum move speeds."""
Expand Down Expand Up @@ -532,11 +535,12 @@ def set_last_location(
self._last_location = location
self._last_mount = mount

def get_deck_definition(self) -> DeckDefinitionV3:
def get_deck_definition(self) -> DeckDefinitionV4:
"""Get the geometry definition of the robot's deck."""
return self._engine_client.state.labware.get_deck_definition()

def get_slot_definition(self, slot: DeckSlotName) -> SlotDefV3:
"""Get the slot definition from the robot's deck."""
return self._engine_client.state.labware.get_slot_definition(slot)

def _ensure_module_location(
Expand Down
13 changes: 7 additions & 6 deletions api/src/opentrons/protocol_api/core/legacy/deck.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,10 @@
import logging
from collections import UserDict
from typing import Dict, Optional, List, Union
from typing_extensions import Protocol
from typing_extensions import Protocol, Final

from opentrons_shared_data.deck import load as load_deck

from opentrons_shared_data.deck import (
DEFAULT_DECK_DEFINITION_VERSION,
load as load_deck,
)
from opentrons_shared_data.deck.dev_types import SlotDefV3
from opentrons_shared_data.labware.dev_types import LabwareUri

Expand All @@ -34,6 +32,9 @@
FIXED_TRASH_ID = "fixedTrash"


DEFAULT_LEGACY_DECK_DEFINITION_VERSION: Final = 3


class DeckItem(Protocol):
@property
def highest_z(self) -> float:
Expand All @@ -50,7 +51,7 @@ class Deck(UserDict): # type: ignore[type-arg]
def __init__(self, deck_type: str) -> None:
super().__init__()
self._definition = load_deck(
name=deck_type, version=DEFAULT_DECK_DEFINITION_VERSION
name=deck_type, version=DEFAULT_LEGACY_DECK_DEFINITION_VERSION
)
self._positions = {}
for slot in self._definition["locations"]["orderedSlots"]:
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import logging
from typing import Dict, List, Optional, Set, Union, cast, Tuple

from opentrons_shared_data.deck.dev_types import DeckDefinitionV3
from opentrons_shared_data.deck.dev_types import DeckDefinitionV4, SlotDefV3
from opentrons_shared_data.labware.dev_types import LabwareDefinition
from opentrons_shared_data.pipette.dev_types import PipetteNameType
from opentrons_shared_data.robot.dev_types import RobotType
Expand Down Expand Up @@ -452,10 +452,14 @@ def get_labware_on_labware(
) -> Optional[LegacyLabwareCore]:
assert False, "get_labware_on_labware only supported on engine core"

def get_deck_definition(self) -> DeckDefinitionV3:
def get_deck_definition(self) -> DeckDefinitionV4:
"""Get the geometry definition of the robot's deck."""
assert False, "get_deck_definition only supported on engine core"

def get_slot_definition(self, slot: DeckSlotName) -> SlotDefV3:
"""Get the slot definition from the robot's deck."""
assert False, "get_slot_definition only supported on engine core"

def get_slot_item(
self, slot_name: DeckSlotName
) -> Union[LegacyLabwareCore, legacy_module_core.LegacyModuleCore, None]:
Expand Down
11 changes: 8 additions & 3 deletions api/src/opentrons/protocol_api/core/protocol.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from abc import abstractmethod, ABC
from typing import Generic, List, Optional, Union, Tuple

from opentrons_shared_data.deck.dev_types import DeckDefinitionV3
from opentrons_shared_data.deck.dev_types import DeckDefinitionV4, SlotDefV3
from opentrons_shared_data.pipette.dev_types import PipetteNameType
from opentrons_shared_data.labware.dev_types import LabwareDefinition
from opentrons_shared_data.robot.dev_types import RobotType
Expand All @@ -28,7 +28,7 @@ class AbstractProtocol(
):
@property
@abstractmethod
def fixed_trash(self) -> LabwareCoreType:
def fixed_trash(self) -> Optional[LabwareCoreType]:
"""Get the fixed trash labware core."""
...

Expand Down Expand Up @@ -154,9 +154,14 @@ def set_last_location(
...

@abstractmethod
def get_deck_definition(self) -> DeckDefinitionV3:
def get_deck_definition(self) -> DeckDefinitionV4:
"""Get the geometry definition of the robot's deck."""

# TODO(jbl 10-30-2023) this method may no longer need to exist post deck config work being completed
@abstractmethod
def get_slot_definition(self, slot: DeckSlotName) -> SlotDefV3:
"""Get the slot definition from the robot's deck."""

@abstractmethod
def get_slot_item(
self, slot_name: DeckSlotName
Expand Down
42 changes: 38 additions & 4 deletions api/src/opentrons/protocol_api/deck.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,13 +65,47 @@ def __init__(
self._core_map = core_map
self._api_version = api_version

self._protocol_core.robot_type

deck_locations = protocol_core.get_deck_definition()["locations"]
# TODO(jbl 10-30-2023) this hardcoding should be removed once slots are refactored to work with deck config
if self._protocol_core.robot_type == "OT-2 Standard":
ordered_slot_ids = [
"1",
"2",
"3",
"4",
"5",
"6",
"7",
"8",
"9",
"10",
"11",
"12",
]
else:
ordered_slot_ids = [
"D1",
"D2",
"D3",
"C1",
"C2",
"C3",
"B1",
"B2",
"B3",
"A1",
"A2",
"A3",
]

self._slot_definitions_by_name = {
slot["id"]: slot for slot in deck_locations["orderedSlots"]
slot_id: self._protocol_core.get_slot_definition(
DeckSlotName.from_primitive(slot_id)
)
for slot_id in ordered_slot_ids
}

deck_locations = protocol_core.get_deck_definition()["locations"]

self._calibration_positions = [
CalibrationPosition(
id=point["id"],
Expand Down
9 changes: 7 additions & 2 deletions api/src/opentrons/protocol_api/instrument_context.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
from opentrons.protocols.advanced_control.mix import mix_from_kwargs
from opentrons.protocols.advanced_control import transfers

from opentrons.protocols.api_support.deck_type import NoTrashDefinedError
from opentrons.protocols.api_support.types import APIVersion
from opentrons.protocols.api_support import instrument
from opentrons.protocols.api_support.util import (
Expand Down Expand Up @@ -84,7 +85,7 @@ def __init__(
broker: LegacyBroker,
api_version: APIVersion,
tip_racks: List[labware.Labware],
trash: labware.Labware,
trash: Optional[labware.Labware],
requested_as: str,
) -> None:
super().__init__(broker)
Expand All @@ -99,7 +100,7 @@ def __init__(
default_dispense=_DEFAULT_DISPENSE_CLEARANCE,
)

self.trash_container = trash
self._trash = trash
self.requested_as = requested_as

@property # type: ignore
Expand Down Expand Up @@ -1422,6 +1423,10 @@ def trash_container(self) -> labware.Labware:
By default, the trash container is in slot A3 on Flex and in slot 12 on OT-2.
"""
if self._trash is None:
raise NoTrashDefinedError(
"No trash container has been defined in this protocol."
)
return self._trash

@trash_container.setter
Expand Down
31 changes: 22 additions & 9 deletions api/src/opentrons/protocol_api/protocol_context.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
from opentrons.commands import protocol_commands as cmds, types as cmd_types
from opentrons.commands.publisher import CommandPublisher, publish
from opentrons.protocols.api_support import instrument as instrument_support
from opentrons.protocols.api_support.deck_type import NoTrashDefinedError
from opentrons.protocols.api_support.types import APIVersion
from opentrons.protocols.api_support.util import (
AxisMaxSpeeds,
Expand Down Expand Up @@ -822,13 +823,19 @@ def load_instrument(
log=logger,
)

trash: Optional[Labware]
try:
trash = self.fixed_trash
except NoTrashDefinedError:
trash = None

instrument = InstrumentContext(
core=instrument_core,
protocol_core=self._core,
broker=self._broker,
api_version=self._api_version,
tip_racks=tip_racks,
trash=self.fixed_trash,
trash=trash,
requested_as=instrument_name,
)

Expand Down Expand Up @@ -985,17 +992,23 @@ def fixed_trash(self) -> Labware:
It has one well and should be accessed like labware in your protocol.
e.g. ``protocol.fixed_trash['A1']``
"""
return self._core_map.get(self._core.fixed_trash)
fixed_trash = self._core_map.get(self._core.fixed_trash)
if fixed_trash is None:
raise NoTrashDefinedError(
"No trash container has been defined in this protocol."
)
return fixed_trash

def _load_fixed_trash(self) -> None:
fixed_trash_core = self._core.fixed_trash
fixed_trash = Labware(
core=fixed_trash_core,
api_version=self._api_version,
protocol_core=self._core,
core_map=self._core_map,
)
self._core_map.add(fixed_trash_core, fixed_trash)
if fixed_trash_core is not None:
fixed_trash = Labware(
core=fixed_trash_core,
api_version=self._api_version,
protocol_core=self._core,
core_map=self._core_map,
)
self._core_map.add(fixed_trash_core, fixed_trash)

@requires_version(2, 5)
def set_rail_lights(self, on: bool) -> None:
Expand Down
Loading

0 comments on commit 8707cdc

Please sign in to comment.