Skip to content

Commit

Permalink
Merge branch 'edge' into E2E_Transfer_and_Command_update
Browse files Browse the repository at this point in the history
  • Loading branch information
alexjoel42 authored Jan 2, 2025
2 parents f9592a5 + 576501a commit e60771b
Show file tree
Hide file tree
Showing 42 changed files with 2,588 additions and 129 deletions.
115 changes: 115 additions & 0 deletions api/src/opentrons/protocol_api/core/engine/protocol.py
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,9 @@ def load_labware(
)
# FIXME(jbl, 2023-08-14) validating after loading the object issue
validation.ensure_definition_is_labware(load_result.definition)
validation.ensure_definition_is_not_lid_after_api_version(
self.api_version, load_result.definition
)

# FIXME(mm, 2023-02-21):
#
Expand Down Expand Up @@ -322,6 +325,52 @@ def load_adapter(

return labware_core

def load_lid(
self,
load_name: str,
location: LabwareCore,
namespace: Optional[str],
version: Optional[int],
) -> LabwareCore:
"""Load an individual lid using its identifying parameters. Must be loaded on an existing Labware."""
load_location = self._convert_labware_location(location=location)
custom_labware_params = (
self._engine_client.state.labware.find_custom_labware_load_params()
)
namespace, version = load_labware_params.resolve(
load_name, namespace, version, custom_labware_params
)
load_result = self._engine_client.execute_command_without_recovery(
cmd.LoadLidParams(
loadName=load_name,
location=load_location,
namespace=namespace,
version=version,
)
)
# FIXME(chb, 2024-12-06) validating after loading the object issue
validation.ensure_definition_is_lid(load_result.definition)

deck_conflict.check(
engine_state=self._engine_client.state,
new_labware_id=load_result.labwareId,
existing_disposal_locations=self._disposal_locations,
# TODO: We can now fetch these IDs from engine too.
# See comment in self.load_labware().
#
# Wrapping .keys() in list() is just to make Decoy verification easier.
existing_labware_ids=list(self._labware_cores_by_id.keys()),
existing_module_ids=list(self._module_cores_by_id.keys()),
)

labware_core = LabwareCore(
labware_id=load_result.labwareId,
engine_client=self._engine_client,
)

self._labware_cores_by_id[labware_core.labware_id] = labware_core
return labware_core

def move_labware(
self,
labware_core: LabwareCore,
Expand Down Expand Up @@ -644,6 +693,72 @@ def set_last_location(
self._last_location = location
self._last_mount = mount

def load_lid_stack(
self,
load_name: str,
location: Union[DeckSlotName, StagingSlotName, LabwareCore],
quantity: int,
namespace: Optional[str],
version: Optional[int],
) -> LabwareCore:
"""Load a Stack of Lids to a given location, creating a Lid Stack."""
if quantity < 1:
raise ValueError(
"When loading a lid stack quantity cannot be less than one."
)
if isinstance(location, DeckSlotName) or isinstance(location, StagingSlotName):
load_location = self._convert_labware_location(location=location)
else:
if isinstance(location, LabwareCore):
load_location = self._convert_labware_location(location=location)
else:
raise ValueError(
"Expected type of Labware Location for lid stack must be Labware, not Legacy Labware or Well."
)

custom_labware_params = (
self._engine_client.state.labware.find_custom_labware_load_params()
)
namespace, version = load_labware_params.resolve(
load_name, namespace, version, custom_labware_params
)

load_result = self._engine_client.execute_command_without_recovery(
cmd.LoadLidStackParams(
loadName=load_name,
location=load_location,
namespace=namespace,
version=version,
quantity=quantity,
)
)

# FIXME(CHB, 2024-12-04) just like load labware and load adapter we have a validating after loading the object issue
validation.ensure_definition_is_lid(load_result.definition)

deck_conflict.check(
engine_state=self._engine_client.state,
new_labware_id=load_result.stackLabwareId,
existing_disposal_locations=self._disposal_locations,
# TODO (spp, 2023-11-27): We've been using IDs from _labware_cores_by_id
# and _module_cores_by_id instead of getting the lists directly from engine
# because of the chance of engine carrying labware IDs from LPC too.
# But with https://github.com/Opentrons/opentrons/pull/13943,
# & LPC in maintenance runs, we can now rely on engine state for these IDs too.
# Wrapping .keys() in list() is just to make Decoy verification easier.
existing_labware_ids=list(self._labware_cores_by_id.keys()),
existing_module_ids=list(self._module_cores_by_id.keys()),
)

labware_core = LabwareCore(
labware_id=load_result.stackLabwareId,
engine_client=self._engine_client,
)

self._labware_cores_by_id[labware_core.labware_id] = labware_core

return labware_core

def get_deck_definition(self) -> DeckDefinitionV5:
"""Get the geometry definition of the robot's deck."""
return self._engine_client.state.labware.get_deck_definition()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,13 @@
from opentrons_shared_data.pipette.types import PipetteNameType
from opentrons_shared_data.robot.types import RobotType

from opentrons.types import DeckSlotName, StagingSlotName, Location, Mount, Point
from opentrons.types import (
DeckSlotName,
StagingSlotName,
Location,
Mount,
Point,
)
from opentrons.util.broker import Broker
from opentrons.hardware_control import SyncHardwareAPI
from opentrons.hardware_control.modules import AbstractModule, ModuleModel, ModuleType
Expand Down Expand Up @@ -267,6 +273,16 @@ def load_adapter(
"""Load an adapter using its identifying parameters"""
raise APIVersionError(api_element="Loading adapter")

def load_lid(
self,
load_name: str,
location: LegacyLabwareCore,
namespace: Optional[str],
version: Optional[int],
) -> LegacyLabwareCore:
"""Load an individual lid labware using its identifying parameters. Must be loaded on a labware."""
raise APIVersionError(api_element="Loading lid")

def load_robot(self) -> None: # type: ignore
"""Load an adapter using its identifying parameters"""
raise APIVersionError(api_element="Loading robot")
Expand Down Expand Up @@ -478,6 +494,17 @@ def set_last_location(
self._last_location = location
self._last_mount = mount

def load_lid_stack(
self,
load_name: str,
location: Union[DeckSlotName, StagingSlotName, LegacyLabwareCore],
quantity: int,
namespace: Optional[str],
version: Optional[int],
) -> LegacyLabwareCore:
"""Load a Stack of Lids to a given location, creating a Lid Stack."""
raise APIVersionError(api_element="Lid stack")

def get_module_cores(self) -> List[legacy_module_core.LegacyModuleCore]:
"""Get loaded module cores."""
return self._module_cores
Expand Down
30 changes: 29 additions & 1 deletion api/src/opentrons/protocol_api/core/protocol.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,13 @@
from opentrons_shared_data.labware.types import LabwareDefinition
from opentrons_shared_data.robot.types import RobotType

from opentrons.types import DeckSlotName, StagingSlotName, Location, Mount, Point
from opentrons.types import (
DeckSlotName,
StagingSlotName,
Location,
Mount,
Point,
)
from opentrons.hardware_control import SyncHardwareAPI
from opentrons.hardware_control.modules.types import ModuleModel
from opentrons.protocols.api_support.util import AxisMaxSpeeds
Expand Down Expand Up @@ -94,6 +100,17 @@ def load_adapter(
"""Load an adapter using its identifying parameters"""
...

@abstractmethod
def load_lid(
self,
load_name: str,
location: LabwareCoreType,
namespace: Optional[str],
version: Optional[int],
) -> LabwareCoreType:
"""Load an individual lid labware using its identifying parameters. Must be loaded on a labware."""
...

@abstractmethod
def move_labware(
self,
Expand Down Expand Up @@ -191,6 +208,17 @@ def set_last_location(
) -> None:
...

@abstractmethod
def load_lid_stack(
self,
load_name: str,
location: Union[DeckSlotName, StagingSlotName, LabwareCoreType],
quantity: int,
namespace: Optional[str],
version: Optional[int],
) -> LabwareCoreType:
...

@abstractmethod
def get_deck_definition(self) -> DeckDefinitionV5:
"""Get the geometry definition of the robot's deck."""
Expand Down
74 changes: 74 additions & 0 deletions api/src/opentrons/protocol_api/labware.py
Original file line number Diff line number Diff line change
Expand Up @@ -544,6 +544,7 @@ def load_labware(
self,
name: str,
label: Optional[str] = None,
lid: Optional[str] = None,
namespace: Optional[str] = None,
version: Optional[int] = None,
) -> Labware:
Expand Down Expand Up @@ -573,6 +574,20 @@ def load_labware(

self._core_map.add(labware_core, labware)

if lid is not None:
if self._api_version < validation.LID_STACK_VERSION_GATE:
raise APIVersionError(
api_element="Loading a Lid on a Labware",
until_version="2.23",
current_version=f"{self._api_version}",
)
self._protocol_core.load_lid(
load_name=lid,
location=labware_core,
namespace=namespace,
version=version,
)

return labware

@requires_version(2, 15)
Expand All @@ -597,6 +612,65 @@ def load_labware_from_definition(
label=label,
)

@requires_version(2, 23)
def load_lid_stack(
self,
load_name: str,
quantity: int,
namespace: Optional[str] = None,
version: Optional[int] = None,
) -> Labware:
"""
Load a stack of Lids onto a valid Deck Location or Adapter.
:param str load_name: A string to use for looking up a lid definition.
You can find the ``load_name`` for any standard lid on the Opentrons
`Labware Library <https://labware.opentrons.com>`_.
:param int quantity: The quantity of lids to be loaded in the stack.
:param str namespace: The namespace that the lid labware definition belongs to.
If unspecified, the API will automatically search two namespaces:
- ``"opentrons"``, to load standard Opentrons labware definitions.
- ``"custom_beta"``, to load custom labware definitions created with the
`Custom Labware Creator <https://labware.opentrons.com/create>`__.
You might need to specify an explicit ``namespace`` if you have a custom
definition whose ``load_name`` is the same as an Opentrons-verified
definition, and you want to explicitly choose one or the other.
:param version: The version of the labware definition. You should normally
leave this unspecified to let ``load_lid_stack()`` choose a version
automatically.
:return: The initialized and loaded labware object representing the Lid Stack.
"""
if self._api_version < validation.LID_STACK_VERSION_GATE:
raise APIVersionError(
api_element="Loading a Lid Stack",
until_version="2.23",
current_version=f"{self._api_version}",
)

load_location = self._core

load_name = validation.ensure_lowercase_name(load_name)

result = self._protocol_core.load_lid_stack(
load_name=load_name,
location=load_location,
quantity=quantity,
namespace=namespace,
version=version,
)

labware = Labware(
core=result,
api_version=self._api_version,
protocol_core=self._protocol_core,
core_map=self._core_map,
)
return labware

def set_calibration(self, delta: Point) -> None:
"""
An internal, deprecated method used for updating the labware offset.
Expand Down
14 changes: 14 additions & 0 deletions api/src/opentrons/protocol_api/module_contexts.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ def load_labware(
namespace: Optional[str] = None,
version: Optional[int] = None,
adapter: Optional[str] = None,
lid: Optional[str] = None,
) -> Labware:
"""Load a labware onto the module using its load parameters.
Expand Down Expand Up @@ -180,6 +181,19 @@ def load_labware(
version=version,
location=load_location,
)
if lid is not None:
if self._api_version < validation.LID_STACK_VERSION_GATE:
raise APIVersionError(
api_element="Loading a lid on a Labware",
until_version="2.23",
current_version=f"{self._api_version}",
)
self._protocol_core.load_lid(
load_name=lid,
location=labware_core,
namespace=namespace,
version=version,
)

if isinstance(self._core, LegacyModuleCore):
labware = self._core.add_labware_core(cast(LegacyLabwareCore, labware_core))
Expand Down
Loading

0 comments on commit e60771b

Please sign in to comment.