Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(app, api, shared-data, robot-server): Add module fixtures to deck configuration #14684

Merged
merged 86 commits into from
Apr 15, 2024
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
86 commits
Select commit Hold shift + click to select a range
3776fdf
initial adding of modules and serial numbers
CaseyBatten Mar 12, 2024
c36543f
transition server router and maintenance run to deck def v5
CaseyBatten Mar 13, 2024
bceeb45
robot server api and shared data updates to handle modules
CaseyBatten Mar 14, 2024
ecccea0
schema and deck def updates with addressable area logic added
CaseyBatten Mar 15, 2024
7f110f6
remaining AA defs and module fixture updates
CaseyBatten Mar 18, 2024
110c745
account for AddressableAreaLocation as valid load module location
CaseyBatten Mar 18, 2024
aca73c4
validate all possible module fixture locations from API
CaseyBatten Mar 19, 2024
a8b71f9
update module fixture and addressable area defs to include version nu…
CaseyBatten Mar 20, 2024
934b28a
introduction of fixture groups to the v5 deck configuration schema
CaseyBatten Mar 20, 2024
64b5208
add cutout fixtures and helpers for module fixtures
b-cooper Mar 18, 2024
46655e2
v5 ot3 deck definition fixup
ahiuchingau Mar 20, 2024
9e4b15c
module fixtures and AA's are versioned
b-cooper Mar 20, 2024
0ef5d70
Merge branch 'add_modules_to_deck_config' of github.com:Opentrons/ope…
b-cooper Mar 20, 2024
b42ca46
test fixes and move addressable area validation to engine
CaseyBatten Mar 20, 2024
93ff8af
Merge branch 'add_modules_to_deck_config' of https://github.com/Opent…
CaseyBatten Mar 20, 2024
de28cbe
do not provide add area for thermocycler v2
ahiuchingau Mar 20, 2024
28fd16e
validation improvements and linting fixes
CaseyBatten Mar 21, 2024
26e64bd
add module fixtures to configurator component
b-cooper Mar 21, 2024
4ebcade
persistence directory migrations v4 for new deck configuration
CaseyBatten Mar 21, 2024
1bbd44c
build up deck configurator module support
b-cooper Mar 21, 2024
06b8f18
Merge branch 'add_modules_to_deck_config' of github.com:Opentrons/ope…
b-cooper Mar 21, 2024
ea51ac1
api and robot server test fixes and engine improvements
CaseyBatten Mar 22, 2024
a5539a6
confest updates with api test fixes and json corrections
CaseyBatten Mar 25, 2024
577d709
shared data lint fix
CaseyBatten Mar 25, 2024
9ed5339
Merge branch 'add_modules_to_deck_config' of github.com:Opentrons/ope…
b-cooper Mar 26, 2024
7a146f0
remove unused configuration imports
b-cooper Mar 26, 2024
a966d37
Merge branch 'edge' into add_modules_to_deck_config
b-cooper Mar 26, 2024
0974d69
fixture height adjustments and type check test fixes
CaseyBatten Mar 26, 2024
c62525c
start fixing up unit tests and only show fixtures when possible
b-cooper Mar 27, 2024
93ef32f
repair last add fixture modal test
b-cooper Mar 28, 2024
d86ce7b
start implement handle click remove for thermocycler
b-cooper Mar 28, 2024
32981d9
configure and remove all modules from device details
b-cooper Mar 28, 2024
b69fdc8
list modules in table properly
b-cooper Mar 28, 2024
35355e9
table alignment
b-cooper Mar 29, 2024
696029e
begin to add support in location conflict modal
b-cooper Mar 29, 2024
4ea0518
fix module conflict calculation to unblock protocol setup
b-cooper Apr 1, 2024
1977256
allow location conflict to grab serial number of first matching module
b-cooper Apr 1, 2024
b2783ca
addressable area offsets and engine consolidation of references to lo…
CaseyBatten Apr 1, 2024
746600e
reformat deck configuration setup list on desktop and remove moam hel…
b-cooper Apr 2, 2024
0fc0f57
Merge branch 'add_modules_to_deck_config' of github.com:Opentrons/ope…
b-cooper Apr 2, 2024
1d5aaee
remove multiples of a module modal from ODD and tests
b-cooper Apr 3, 2024
2b4ddd9
fixture height calculation correction and fixture group map structure
CaseyBatten Apr 3, 2024
f809136
Merge branch 'add_modules_to_deck_config' of github.com:Opentrons/ope…
b-cooper Apr 3, 2024
2c8fe4a
fixture group shape update and validation
CaseyBatten Apr 3, 2024
ce8f7b3
Merge branch 'add_modules_to_deck_config' of github.com:Opentrons/ope…
b-cooper Apr 4, 2024
2df67ee
update grouping to include map
b-cooper Apr 4, 2024
76a4ca9
fix fixture group display location
b-cooper Apr 4, 2024
c7bedcd
fixup groupings in the protocol setup, and resolving module conflicts…
b-cooper Apr 5, 2024
33baf87
cleaning up across stack and test fixes
CaseyBatten Apr 5, 2024
2dce8f3
Merge branch 'add_modules_to_deck_config' of https://github.com/Opent…
CaseyBatten Apr 5, 2024
3e52951
add mag block with staging fixture
b-cooper Apr 5, 2024
b8697eb
mag with staging area fe support
b-cooper Apr 5, 2024
4bc2e97
allow for module select in location conflict modal
b-cooper Apr 5, 2024
3d038a5
test fixes for api and deck configuration validation
CaseyBatten Apr 8, 2024
562b448
clean up js lint
b-cooper Apr 10, 2024
65582a6
Merge branch 'edge' into add_modules_to_deck_config
b-cooper Apr 10, 2024
52a60e8
remove double listing of modules from odd protocol hardware reqs
b-cooper Apr 10, 2024
38a5a9c
deck slot test fixes to include deck config and update of tavern test…
CaseyBatten Apr 10, 2024
4e911c7
Merge branch 'add_modules_to_deck_config' of https://github.com/Opent…
CaseyBatten Apr 10, 2024
d1cc543
addition of missing notes key to updated tavern test
CaseyBatten Apr 10, 2024
4b842ff
lint corrections for app hardware and shared data
CaseyBatten Apr 10, 2024
c8fa3fd
fix up redundancies in tests for setupmodules list
b-cooper Apr 10, 2024
6f945ff
fix up broken tests in add fixture modal
b-cooper Apr 10, 2024
a72a4a5
clean up lint
b-cooper Apr 11, 2024
c0b29c1
ot2 deck def and shared data test fix
CaseyBatten Apr 11, 2024
c80c04b
Merge branch 'add_modules_to_deck_config' of https://github.com/Opent…
CaseyBatten Apr 11, 2024
921ef43
filter for unconfigured module matches in location conflict modal
b-cooper Apr 11, 2024
3014bc8
fix up useModuleRenderInfoById to include config in matching
b-cooper Apr 11, 2024
cc3bc10
properly filter module fixture options on choose module to configure …
b-cooper Apr 11, 2024
2a5daa6
Merge branch 'add_modules_to_deck_config' of github.com:Opentrons/ope…
b-cooper Apr 11, 2024
711ba14
format js
b-cooper Apr 11, 2024
7908112
fix failing test
b-cooper Apr 11, 2024
4f4510f
fix location conflict add module when fixture grouped
b-cooper Apr 12, 2024
ce3116c
Merge branch 'edge' into add_modules_to_deck_config
b-cooper Apr 12, 2024
584986f
resolve multiples of a module calibration mismatch using deck config
CaseyBatten Apr 12, 2024
12e7aed
Merge branch 'add_modules_to_deck_config' of https://github.com/Opent…
CaseyBatten Apr 12, 2024
8afeef6
add initial support for location select in module calibration
b-cooper Apr 12, 2024
44ec408
Merge branch 'add_modules_to_deck_config' of github.com:Opentrons/ope…
b-cooper Apr 12, 2024
2fc5e1c
robot server deck slot standardization test correction
CaseyBatten Apr 15, 2024
38bde3f
Merge branch 'add_modules_to_deck_config' of https://github.com/Opent…
CaseyBatten Apr 15, 2024
43b483d
fix deck hardware item for module fixture in wrong location
b-cooper Apr 15, 2024
38133e9
Merge branch 'add_modules_to_deck_config' of github.com:Opentrons/ope…
b-cooper Apr 15, 2024
b7e50a5
lint and format
b-cooper Apr 15, 2024
a411009
fix ODD dashboard deck configurator
b-cooper Apr 15, 2024
ad69388
add fixture option should include usb port info on ODD
b-cooper Apr 15, 2024
0c1759b
Merge branch 'edge' into add_modules_to_deck_config
b-cooper Apr 15, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions api/src/opentrons/calibration_storage/deck_configuration.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
class _CutoutFixturePlacementModel(pydantic.BaseModel):
cutoutId: str
cutoutFixtureId: str
opentronsModuleSerialNumber: Optional[str]


class _DeckConfigurationModel(pydantic.BaseModel):
Expand All @@ -26,7 +27,9 @@ def serialize_deck_configuration(
data = _DeckConfigurationModel.construct(
cutoutFixtures=[
_CutoutFixturePlacementModel.construct(
cutoutId=e.cutout_id, cutoutFixtureId=e.cutout_fixture_id
cutoutId=e.cutout_id,
cutoutFixtureId=e.cutout_fixture_id,
opentronsModuleSerialNumber=e.opentrons_modules_serial_number,
)
for e in cutout_fixture_placements
],
Expand All @@ -50,7 +53,9 @@ def deserialize_deck_configuration(
else:
cutout_fixture_placements = [
CutoutFixturePlacement(
cutout_id=e.cutoutId, cutout_fixture_id=e.cutoutFixtureId
cutout_id=e.cutoutId,
cutout_fixture_id=e.cutoutFixtureId,
opentrons_modules_serial_number=e.opentronsModuleSerialNumber,
)
for e in parsed.cutoutFixtures
]
Expand Down
1 change: 1 addition & 0 deletions api/src/opentrons/calibration_storage/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,4 @@ class UriDetails:
class CutoutFixturePlacement:
cutout_fixture_id: str
cutout_id: str
opentrons_modules_serial_number: typing.Optional[str]
17 changes: 17 additions & 0 deletions api/src/opentrons/hardware_control/modules/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
Tuple,
Awaitable,
Union,
Set,
cast,
TYPE_CHECKING,
)
Expand Down Expand Up @@ -64,6 +65,22 @@ def from_model(cls, model: ModuleModel) -> ModuleType:
if isinstance(model, MagneticBlockModel):
return cls.MAGNETIC_BLOCK

@classmethod
def to_module_fixture_id(cls, module_type: ModuleType) -> str:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks like these cutout fixture ids still need the model versions added in

if module_type == ModuleType.THERMOCYCLER:
# Thermocyclers are "loaded" in B1 only
return "thermocyclerModuleFront"
if module_type == ModuleType.TEMPERATURE:
return "temperatureModule"
if module_type == ModuleType.HEATER_SHAKER:
return "heaterShakerModule"
if module_type == ModuleType.MAGNETIC_BLOCK:
return "magneticBlockModule"
else:
raise ValueError(
f"Module Type {module_type} does not have a related fixture ID."
)


class MagneticModuleModel(str, Enum):
MAGNETIC_V1: str = "magneticModuleV1"
Expand Down
44 changes: 34 additions & 10 deletions api/src/opentrons/protocol_api/core/engine/protocol.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
from typing import Dict, Optional, Type, Union, List, Tuple, TYPE_CHECKING

from opentrons.protocol_engine.commands import LoadModuleResult
from opentrons_shared_data.deck.dev_types import DeckDefinitionV4, SlotDefV3
from opentrons_shared_data.deck.dev_types import DeckDefinitionV5, SlotDefV3
from opentrons.protocol_engine.resources import deck_configuration_provider
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 @@ -393,6 +394,7 @@ def load_module(
model: ModuleModel,
deck_slot: Optional[DeckSlotName],
configuration: Optional[str],
addressable_area: Optional[str],
) -> Union[ModuleCore, NonConnectedModuleCore]:
"""Load a module into the protocol."""
assert configuration is None, "Module `configuration` is deprecated"
Expand All @@ -411,10 +413,16 @@ def load_module(
normalized_deck_slot = deck_slot.to_equivalent_for_robot_type(robot_type)
self._ensure_module_location(normalized_deck_slot, module_type)

result = self._engine_client.load_module(
model=EngineModuleModel(model),
location=DeckSlotLocation(slotName=normalized_deck_slot),
)
if robot_type == "OT-3 Standard" and isinstance(addressable_area, str):
result = self._engine_client.load_module(
model=EngineModuleModel(model),
location=AddressableAreaLocation(addressableAreaName=addressable_area),
)
else:
result = self._engine_client.load_module(
model=EngineModuleModel(model),
location=DeckSlotLocation(slotName=normalized_deck_slot),
)

module_core = self._get_module_core(load_module_result=result, model=model)

Expand Down Expand Up @@ -602,7 +610,7 @@ def set_last_location(
self._last_location = location
self._last_mount = mount

def get_deck_definition(self) -> DeckDefinitionV4:
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 All @@ -625,10 +633,26 @@ def get_staging_slot_definitions(self) -> Dict[str, SlotDefV3]:
def _ensure_module_location(
self, slot: DeckSlotName, module_type: ModuleType
) -> None:
slot_def = self.get_slot_definition(slot)
compatible_modules = slot_def["compatibleModuleTypes"]
if module_type.value not in compatible_modules:
raise ValueError(f"A {module_type.value} cannot be loaded into slot {slot}")
if self._engine_client.state.config.robot_type == "OT-2 Standard":
slot_def = self.get_slot_definition(slot)
compatible_modules = slot_def["compatibleModuleTypes"]
if module_type.value not in compatible_modules:
raise ValueError(
f"A {module_type.value} cannot be loaded into slot {slot}"
)
else:
cutout_fixture_id = ModuleType.to_module_fixture_id(module_type)
module_fixture = deck_configuration_provider.get_cutout_fixture(
cutout_fixture_id,
self._engine_client.state.addressable_areas.state.deck_definition,
)
cutout_id = self._engine_client.state.addressable_areas.get_cutout_id_by_deck_slot_name(
slot
)
if cutout_id not in module_fixture["mayMountTo"]:
raise ValueError(
f"A {module_type.value} cannot be loaded into slot {slot}"
)

def get_slot_item(
self, slot_name: Union[DeckSlotName, StagingSlotName]
Expand Down
3 changes: 3 additions & 0 deletions api/src/opentrons/protocol_api/core/legacy/deck.py
Original file line number Diff line number Diff line change
Expand Up @@ -278,8 +278,11 @@ def resolve_module_location(
if isinstance(location, str) or isinstance(location, int):
slot_def = self.get_slot_definition(str(location))
compatible_modules = slot_def["compatibleModuleTypes"]
cutout_fixture_id = ModuleType.to_module_fixture_id(module_type)
if module_type.value in compatible_modules:
return location
elif cutout_fixture_id == slot_def["id"]:
return location
else:
raise ValueError(
f"A {dn_from_type[module_type]} cannot be loaded"
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 DeckDefinitionV4, SlotDefV3
from opentrons_shared_data.deck.dev_types import DeckDefinitionV5, 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 @@ -288,6 +288,7 @@ def load_module(
model: ModuleModel,
deck_slot: Optional[DeckSlotName],
configuration: Optional[str],
addressable_area: Optional[str],
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this seems extraneous right? we aren't really loading a module into an addressable area. It's more that we're loading it into a cutout right?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could be moved elsewhere potentially. Right now the logic flow is:

  1. PAPI load_module command begins the load process and supplies a deck location.
  2. Module fixture location is converted to a valid addressable area for that Module, if one exists (could be None, like for OT-2). Up until this point we do the same thing for fixtures commands like load_trash_bin(...).
  3. Engine core load_module is passed the identified addressable_area value (Str | None)
  4. Legacy load_module engine method doesn't use this value, current load_module engine method will supply the engine client load_module method with the validated addressable area if it is not None

I could probably collapse this down to occur inside the engine-side load_module logic instead if we want.

) -> legacy_module_core.LegacyModuleCore:
"""Load a module."""
resolved_type = ModuleType.from_model(model)
Expand Down Expand Up @@ -491,7 +492,7 @@ def get_labware_on_labware(
) -> Optional[LegacyLabwareCore]:
assert False, "get_labware_on_labware only supported on engine core"

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

Expand Down
5 changes: 3 additions & 2 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, Dict, TYPE_CHECKING

from opentrons_shared_data.deck.dev_types import DeckDefinitionV4, SlotDefV3
from opentrons_shared_data.deck.dev_types import DeckDefinitionV5, 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 Down Expand Up @@ -119,6 +119,7 @@ def load_module(
model: ModuleModel,
deck_slot: Optional[DeckSlotName],
configuration: Optional[str],
addressable_area: Optional[str],
) -> ModuleCoreType:
...

Expand Down Expand Up @@ -188,7 +189,7 @@ def set_last_location(
...

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

@abstractmethod
Expand Down
5 changes: 5 additions & 0 deletions api/src/opentrons/protocol_api/protocol_context.py
Original file line number Diff line number Diff line change
Expand Up @@ -800,10 +800,15 @@ def load_module(
if isinstance(deck_slot, StagingSlotName):
raise ValueError("Cannot load a module onto a staging slot.")

addressable_area = validation.ensure_and_convert_module_fixture_location(
location, self._api_version, self._core.robot_type, requested_model
)

module_core = self._core.load_module(
model=requested_model,
deck_slot=deck_slot,
configuration=configuration,
addressable_area=addressable_area,
)

module_context = _create_module_context(
Expand Down
107 changes: 107 additions & 0 deletions api/src/opentrons/protocol_api/validation.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,10 @@ class InvalidTrashBinLocationError(ValueError):
"""An error raised when attempting to load trash bins in invalid slots."""


class InvalidFixtureLocationError(ValueError):
"""An error raised when attempting to load a fixture in an invalid cutout."""


def ensure_mount_for_pipette(
mount: Union[str, Mount, None], pipette: PipetteNameType
) -> Mount:
Expand Down Expand Up @@ -332,6 +336,109 @@ def ensure_and_convert_trash_bin_location(
return map_trash_bin_addressable_area[slot_name_ot3]


def ensure_and_convert_module_fixture_location(
deck_slot: Optional[Union[int, str]],
api_version: APIVersion,
robot_type: RobotType,
model: ModuleModel,
) -> Optional[str]:
"""Ensure module fixture load location is valid.

Also, convert the deck slot to a valid module fixture addressable area.
"""

if robot_type == "OT-2 Standard":
# OT-2 Utilizes the existing compatibleModulelTypes list of traditional addressable areas
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
# OT-2 Utilizes the existing compatibleModulelTypes list of traditional addressable areas
# OT-2 Utilizes the existing compatibleModuleTypes list of traditional addressable areas

return None

if isinstance(model, MagneticBlockModel):
valid_slots = [
DeckSlotName(slot)
for slot in [
"A1",
"B1",
"C1",
"D1",
"A2",
"B2",
"C2",
"D2",
"A3",
"B3",
"C3",
"D3",
]
]
addressable_areas = [
"magneticBlockV1A1",
"magneticBlockV1B1",
"magneticBlockV1C1",
"magneticBlockV1D1",
"magneticBlockV1A2",
"magneticBlockV1B2",
"magneticBlockV1C2",
"magneticBlockV1D2",
"magneticBlockV1A3",
"magneticBlockV1B3",
"magneticBlockV1C3",
"magneticBlockV1D3",
]

elif isinstance(model, HeaterShakerModuleModel):
valid_slots = [
DeckSlotName(slot)
for slot in ["A1", "B1", "C1", "D1", "A3", "B3", "C3", "D3"]
]
addressable_areas = [
"heaterShakerV1A1",
"heaterShakerV1B1",
"heaterShakerV1C1",
"heaterShakerV1D1",
"heaterShakerV1A3",
"heaterShakerV1B3",
"heaterShakerV1C3",
"heaterShakerV1D3",
]
elif isinstance(model, TemperatureModuleModel):
valid_slots = [
DeckSlotName(slot)
for slot in ["A1", "B1", "C1", "D1", "A3", "B3", "C3", "D3"]
]
addressable_areas = [
"temperatureModuleV2A1",
"temperatureModuleV2B1",
"temperatureModuleV2C1",
"temperatureModuleV2D1",
"temperatureModuleV2A3",
"temperatureModuleV2B3",
"temperatureModuleV2C3",
"temperatureModuleV2D3",
]
elif isinstance(model, ThermocyclerModuleModel):
return "thermocyclerModuleV2"
else:
return None

map_addressable_area = {
slot: addressable_area
for slot, addressable_area in zip(valid_slots, addressable_areas)
}
if isinstance(deck_slot, int) or isinstance(deck_slot, str):
slot_name_ot3 = ensure_and_convert_deck_slot(deck_slot, api_version, robot_type)
if not isinstance(slot_name_ot3, DeckSlotName):
raise ValueError("Staging areas not permitted for module fixtures.")
if slot_name_ot3 not in valid_slots:
raise InvalidFixtureLocationError(
f"Invalid location: {slot_name_ot3} for fixture: {model.name}."
)

return map_addressable_area[slot_name_ot3]
else:
raise ValueError(
"Location must be provided when loading modules except for the Thermocycler."
)


def ensure_hold_time_seconds(
seconds: Optional[float], minutes: Optional[float]
) -> float:
Expand Down
5 changes: 3 additions & 2 deletions api/src/opentrons/protocol_engine/clients/sync_client.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"""Control a `ProtocolEngine` without async/await."""

from typing import cast, List, Optional, Dict
from typing import cast, List, Optional, Dict, Union

from opentrons_shared_data.pipette.dev_types import PipetteNameType
from opentrons_shared_data.labware.dev_types import LabwareUri
Expand All @@ -25,6 +25,7 @@
Liquid,
NozzleLayoutConfigurationType,
AddressableOffsetVector,
AddressableAreaLocation,
)
from .transports import ChildThreadTransport

Expand Down Expand Up @@ -264,7 +265,7 @@ def move_to_coordinates(
def load_module(
self,
model: ModuleModel,
location: DeckSlotLocation,
location: Union[DeckSlotLocation, AddressableAreaLocation],
) -> commands.LoadModuleResult:
"""Execute a LoadModule command and return the result."""
request = commands.LoadModuleCreate(
Expand Down
Loading
Loading