Skip to content

Commit 49662eb

Browse files
authored
fix(api): Disposal location slot occupation collision checking (#17841)
This addresses part of EXEC-1353 Allows geometry to check for disposal locations when looking for engine slot items.
1 parent 68263e5 commit 49662eb

File tree

2 files changed

+109
-11
lines changed

2 files changed

+109
-11
lines changed

api/src/opentrons/protocol_engine/state/geometry.py

+35
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@
7676
OnAddressableAreaLocationSequenceComponent,
7777
OnCutoutFixtureLocationSequenceComponent,
7878
NotOnDeckLocationSequenceComponent,
79+
AreaType,
7980
labware_location_is_off_deck,
8081
labware_location_is_system,
8182
)
@@ -907,6 +908,32 @@ def _get_potential_fixtures_for_location_occupation(
907908
fixtures = None
908909
return fixtures
909910

911+
def _get_potential_disposal_location_cutout_fixtures(
912+
self, slot_name: DeckSlotName
913+
) -> CutoutFixture | None:
914+
for area in self._addressable_areas.get_all():
915+
if (
916+
self._addressable_areas.get_addressable_area(area).area_type
917+
== AreaType.WASTE_CHUTE
918+
or self._addressable_areas.get_addressable_area(area).area_type
919+
== AreaType.MOVABLE_TRASH
920+
) and slot_name == self._addressable_areas.get_addressable_area_base_slot(
921+
area
922+
):
923+
# Given we only have one Waste Chute fixture and one type of Trash bin fixture it's
924+
# fine to return the first result of our potential fixtures here. This will need to
925+
# change in the future if there multiple trash fixtures that share the same area type.
926+
potential_fixture = (
927+
deck_configuration_provider.get_potential_cutout_fixtures(
928+
area, self._addressable_areas.deck_definition
929+
)[1].pop()
930+
)
931+
return deck_configuration_provider.get_cutout_fixture(
932+
potential_fixture.cutout_fixture_id,
933+
self._addressable_areas.deck_definition,
934+
)
935+
return None
936+
910937
def get_labware_grip_point(
911938
self,
912939
labware_definition: LabwareDefinition,
@@ -1042,6 +1069,7 @@ def get_slot_item(
10421069
maybe_fixture = self._addressable_areas.get_fixture_by_deck_slot_name(
10431070
slot_name
10441071
)
1072+
10451073
# Ignore generic single slot fixtures
10461074
if maybe_fixture and maybe_fixture["id"] in {
10471075
"singleLeftSlot",
@@ -1053,6 +1081,13 @@ def get_slot_item(
10531081
maybe_module = self._modules.get_by_slot(
10541082
slot_name=slot_name,
10551083
) or self._modules.get_overflowed_module_in_slot(slot_name=slot_name)
1084+
1085+
# For situations in which the deck config is none
1086+
if maybe_fixture is None and maybe_labware is None and maybe_module is None:
1087+
# todo(chb 2025-03-19): This can go away once we solve the problem of no deck config in analysis
1088+
maybe_fixture = self._get_potential_disposal_location_cutout_fixtures(
1089+
slot_name
1090+
)
10561091
else:
10571092
# Modules and fixtures can't be loaded on staging slots
10581093
maybe_fixture = None

api/tests/opentrons/protocol_engine/state/test_geometry_view.py

+74-11
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
)
1919

2020
from opentrons_shared_data import get_shared_data_root, load_shared_data
21-
from opentrons_shared_data.deck.types import DeckDefinitionV5
21+
from opentrons_shared_data.deck.types import DeckDefinitionV5, CutoutFixture
2222
from opentrons_shared_data.deck import load as load_deck
2323
from opentrons_shared_data.labware.types import LabwareUri
2424
from opentrons_shared_data.pipette import pipette_definition
@@ -45,7 +45,6 @@
4545
labware_definition_type_adapter,
4646
)
4747
from opentrons_shared_data.labware import load_definition as load_labware_definition
48-
4948
from opentrons.protocol_engine import errors
5049
from opentrons.protocol_engine.types import (
5150
OFF_DECK_LOCATION,
@@ -89,6 +88,9 @@
8988
OnCutoutFixtureLocationSequenceComponent,
9089
InStackerHopperLocation,
9190
PotentialCutoutFixture,
91+
AddressableArea,
92+
AreaType,
93+
AddressableOffsetVector,
9294
)
9395
from opentrons.protocol_engine.commands import (
9496
CommandStatus,
@@ -134,6 +136,7 @@
134136
mountOffset,
135137
)
136138

139+
137140
from .command_fixtures import (
138141
create_comment_command,
139142
)
@@ -2580,28 +2583,88 @@ def test_get_slot_item(
25802583
decoy: Decoy,
25812584
mock_labware_view: LabwareView,
25822585
mock_module_view: ModuleView,
2586+
ot3_standard_deck_def: DeckDefinitionV5,
25832587
subject: GeometryView,
25842588
) -> None:
25852589
"""It should get items in certain slots."""
25862590
labware = LoadedLabware.model_construct(id="cool-labware") # type: ignore[call-arg]
25872591
module = LoadedModule.model_construct(id="cool-module") # type: ignore[call-arg]
2592+
chute_fixture = CutoutFixture(
2593+
id="wasteChuteRightAdapterNoCover",
2594+
mayMountTo=["cutoutD3"],
2595+
providesAddressableAreas={
2596+
"cutoutD3": [
2597+
"1ChannelWasteChute",
2598+
"8ChannelWasteChute",
2599+
"96ChannelWasteChute",
2600+
"gripperWasteChute",
2601+
]
2602+
},
2603+
displayName="Waste Chute Adapter for 96 Channel Pipette or Gripper",
2604+
fixtureGroup={},
2605+
expectOpentronsModuleSerialNumber=False,
2606+
height=124.5,
2607+
)
2608+
chute_area = AddressableArea(
2609+
area_name="1ChannelWasteChute",
2610+
area_type=AreaType.WASTE_CHUTE,
2611+
base_slot=DeckSlotName.SLOT_D3,
2612+
display_name="",
2613+
bounding_box=Dimensions(x=0, y=0, z=0),
2614+
position=AddressableOffsetVector(x=0, y=0, z=0),
2615+
compatible_module_types=[],
2616+
)
2617+
subject._addressable_areas = AddressableAreaView(
2618+
state=AddressableAreaState(
2619+
loaded_addressable_areas_by_name={
2620+
"1ChannelWasteChute": chute_area,
2621+
},
2622+
potential_cutout_fixtures_by_cutout_id={
2623+
"cutoutD3": {
2624+
PotentialCutoutFixture(
2625+
cutout_id="cutoutD3",
2626+
cutout_fixture_id="cool_chute",
2627+
provided_addressable_areas=frozenset({chute_area.area_name}),
2628+
)
2629+
},
2630+
"cutoutC3": {
2631+
PotentialCutoutFixture(
2632+
cutout_id="cutoutC3",
2633+
cutout_fixture_id="cool_trash",
2634+
provided_addressable_areas=frozenset({chute_area.area_name}),
2635+
)
2636+
},
2637+
},
2638+
deck_definition=ot3_standard_deck_def,
2639+
deck_configuration=[
2640+
("cutoutC1", "singleLeftSlot", None),
2641+
("cutoutD1", "singleLeftSlot", None),
2642+
("cutoutD2", "singleCenterSlot", None),
2643+
("cutoutD3", "wasteChuteRightAdapterNoCover", None),
2644+
],
2645+
robot_type=subject._addressable_areas._state.robot_type,
2646+
use_simulated_deck_config=subject._addressable_areas._state.use_simulated_deck_config,
2647+
robot_definition=subject._addressable_areas._state.robot_definition,
2648+
)
2649+
)
25882650

2589-
decoy.when(mock_labware_view.get_by_slot(DeckSlotName.SLOT_1)).then_return(None)
2590-
decoy.when(mock_labware_view.get_by_slot(DeckSlotName.SLOT_2)).then_return(labware)
2591-
decoy.when(mock_labware_view.get_by_slot(DeckSlotName.SLOT_3)).then_return(None)
2651+
decoy.when(mock_labware_view.get_by_slot(DeckSlotName.SLOT_D1)).then_return(None)
2652+
decoy.when(mock_labware_view.get_by_slot(DeckSlotName.SLOT_D2)).then_return(labware)
2653+
decoy.when(mock_labware_view.get_by_slot(DeckSlotName.SLOT_C1)).then_return(None)
25922654

2593-
decoy.when(mock_module_view.get_by_slot(DeckSlotName.SLOT_1)).then_return(None)
2594-
decoy.when(mock_module_view.get_by_slot(DeckSlotName.SLOT_2)).then_return(None)
2595-
decoy.when(mock_module_view.get_by_slot(DeckSlotName.SLOT_3)).then_return(module)
2655+
decoy.when(mock_module_view.get_by_slot(DeckSlotName.SLOT_D1)).then_return(module)
2656+
decoy.when(mock_module_view.get_by_slot(DeckSlotName.SLOT_D2)).then_return(None)
2657+
decoy.when(mock_module_view.get_by_slot(DeckSlotName.SLOT_C1)).then_return(None)
25962658

25972659
assert (
25982660
subject.get_slot_item(
2599-
DeckSlotName.SLOT_1,
2661+
DeckSlotName.SLOT_C1,
26002662
)
26012663
is None
26022664
)
2603-
assert subject.get_slot_item(DeckSlotName.SLOT_2) == labware
2604-
assert subject.get_slot_item(DeckSlotName.SLOT_3) == module
2665+
assert subject.get_slot_item(DeckSlotName.SLOT_D2) == labware
2666+
assert subject.get_slot_item(DeckSlotName.SLOT_D1) == module
2667+
assert subject.get_slot_item(DeckSlotName.SLOT_D3) == chute_fixture
26052668

26062669

26072670
def test_get_slot_item_that_is_overflowed_module(

0 commit comments

Comments
 (0)