From ea51ac1e4c9947dd4151108fe81403691db40f2f Mon Sep 17 00:00:00 2001 From: CaseyBatten Date: Fri, 22 Mar 2024 18:31:17 -0400 Subject: [PATCH] api and robot server test fixes and engine improvements --- .../hardware_control/modules/types.py | 2 +- .../protocol_api/core/legacy/deck.py | 6 +- api/src/opentrons/protocol_api/validation.py | 2 +- .../core/engine/test_protocol_core.py | 140 +++++++++++++++--- .../test_create_protocol_engine.py | 25 ++++ .../persistence/_migrations/v3_to_v4.py | 4 +- .../http_api/persistence/test_reset.py | 6 +- .../http_api/protocols/test_persistence.py | 4 +- shared-data/deck/types/schemaV5.ts | 4 +- shared-data/js/constants.ts | 84 +++++++---- shared-data/js/fixtures.ts | 53 ++++--- .../helpers/getAddressableAreasInProtocol.ts | 5 +- .../opentrons_shared_data/deck/dev_types.py | 2 +- 13 files changed, 256 insertions(+), 81 deletions(-) diff --git a/api/src/opentrons/hardware_control/modules/types.py b/api/src/opentrons/hardware_control/modules/types.py index f4443a43563..7c84c49fed5 100644 --- a/api/src/opentrons/hardware_control/modules/types.py +++ b/api/src/opentrons/hardware_control/modules/types.py @@ -68,7 +68,7 @@ def from_model(cls, model: ModuleModel) -> ModuleType: def to_module_fixture_id(cls, module_type: ModuleType) -> str: if module_type == ModuleType.THERMOCYCLER: # Thermocyclers are "loaded" in B1 only - return "thermocyclerModuleFrontV2" + return "thermocyclerModuleV2Front" if module_type == ModuleType.TEMPERATURE: return "temperatureModuleV2" if module_type == ModuleType.HEATER_SHAKER: diff --git a/api/src/opentrons/protocol_api/core/legacy/deck.py b/api/src/opentrons/protocol_api/core/legacy/deck.py index 2cadb20abb6..685f0f5d553 100644 --- a/api/src/opentrons/protocol_api/core/legacy/deck.py +++ b/api/src/opentrons/protocol_api/core/legacy/deck.py @@ -278,10 +278,12 @@ 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"]: + elif ( + self._definition["robot"]["model"] == "OT-3 Standard" + and ModuleType.to_module_fixture_id(module_type) == slot_def["id"] + ): return location else: raise ValueError( diff --git a/api/src/opentrons/protocol_api/validation.py b/api/src/opentrons/protocol_api/validation.py index 04d6aee34a9..d0384937b02 100644 --- a/api/src/opentrons/protocol_api/validation.py +++ b/api/src/opentrons/protocol_api/validation.py @@ -421,7 +421,7 @@ def ensure_and_convert_module_fixture_location( slot: addressable_area for slot, addressable_area in zip(valid_slots, addressable_areas) } - return map_addressable_area[deck_slot.name] + return map_addressable_area[deck_slot.value] def ensure_hold_time_seconds( diff --git a/api/tests/opentrons/protocol_api/core/engine/test_protocol_core.py b/api/tests/opentrons/protocol_api/core/engine/test_protocol_core.py index 3c153bc623c..d5e71f56f46 100644 --- a/api/tests/opentrons/protocol_api/core/engine/test_protocol_core.py +++ b/api/tests/opentrons/protocol_api/core/engine/test_protocol_core.py @@ -7,7 +7,10 @@ from decoy import Decoy from opentrons_shared_data.deck import load as load_deck -from opentrons_shared_data.deck.dev_types import DeckDefinitionV5, 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 as LabwareDefDict, @@ -1154,7 +1157,7 @@ def test_add_labware_definition( EngineModuleModel.THERMOCYCLER_MODULE_V2, ThermocyclerModuleCore, lazy_fixture("ot3_standard_deck_def"), - DeckSlotName.SLOT_A1, + DeckSlotName.SLOT_B1, "OT-3 Standard", ), ( @@ -1193,12 +1196,22 @@ def test_load_module( [mock_hw_mod_1, mock_hw_mod_2] ) - decoy.when(subject.get_slot_definition(slot_name)).then_return( - cast( - SlotDefV3, - {"compatibleModuleTypes": [ModuleType.from_model(requested_model)]}, + if robot_type == "OT-2 Standard": + decoy.when(subject.get_slot_definition(slot_name)).then_return( + cast( + SlotDefV3, + {"compatibleModuleTypes": [ModuleType.from_model(requested_model)]}, + ) ) - ) + else: + decoy.when( + mock_engine_client.state.addressable_areas.state.deck_definition + ).then_return(deck_def) + decoy.when( + mock_engine_client.state.addressable_areas.get_cutout_id_by_deck_slot_name( + slot_name + ) + ).then_return("cutout" + slot_name.value) decoy.when(mock_engine_client.state.config.robot_type).then_return(robot_type) @@ -1269,14 +1282,6 @@ def test_load_module( DeckSlotName.SLOT_D2, "OT-3 Standard", ), - ( - MagneticModuleModel.MAGNETIC_V2, - EngineModuleModel.MAGNETIC_MODULE_V2, - MagneticModuleCore, - lazy_fixture("ot3_standard_deck_def"), - DeckSlotName.SLOT_A2, - "OT-3 Standard", - ), ( ThermocyclerModuleModel.THERMOCYCLER_V1, EngineModuleModel.THERMOCYCLER_MODULE_V1, @@ -1327,9 +1332,19 @@ def test_load_module_raises_wrong_location( decoy.when(mock_engine_client.state.config.robot_type).then_return(robot_type) - decoy.when(subject.get_slot_definition(slot_name)).then_return( - cast(SlotDefV3, {"compatibleModuleTypes": []}) - ) + if robot_type == "OT-2 Standard": + decoy.when(subject.get_slot_definition(slot_name)).then_return( + cast(SlotDefV3, {"compatibleModuleTypes": []}) + ) + else: + decoy.when( + mock_engine_client.state.addressable_areas.state.deck_definition + ).then_return(deck_def) + decoy.when( + mock_engine_client.state.addressable_areas.get_cutout_id_by_deck_slot_name( + slot_name + ) + ).then_return("cutout" + slot_name.value) with pytest.raises( ValueError, @@ -1342,6 +1357,75 @@ def test_load_module_raises_wrong_location( ) +@pytest.mark.parametrize( + ( + "requested_model", + "engine_model", + "expected_core_cls", + "deck_def", + "slot_name", + "robot_type", + ), + [ + ( + MagneticModuleModel.MAGNETIC_V2, + EngineModuleModel.MAGNETIC_MODULE_V2, + MagneticModuleCore, + lazy_fixture("ot3_standard_deck_def"), + DeckSlotName.SLOT_A2, + "OT-3 Standard", + ), + ], +) +def test_load_module_raises_module_fixture_id_does_not_exist( + decoy: Decoy, + mock_engine_client: EngineClient, + mock_sync_hardware_api: SyncHardwareAPI, + requested_model: ModuleModel, + engine_model: EngineModuleModel, + expected_core_cls: Type[ModuleCore], + subject: ProtocolCore, + deck_def: DeckDefinitionV5, + slot_name: DeckSlotName, + robot_type: RobotType, +) -> None: + """It should issue a load module engine command and raise an error for unmatched fixtures.""" + mock_hw_mod_1 = decoy.mock(cls=AbstractModule) + mock_hw_mod_2 = decoy.mock(cls=AbstractModule) + + decoy.when(mock_hw_mod_1.device_info).then_return({"serial": "abc123"}) + decoy.when(mock_hw_mod_2.device_info).then_return({"serial": "xyz789"}) + decoy.when(mock_sync_hardware_api.attached_modules).then_return( + [mock_hw_mod_1, mock_hw_mod_2] + ) + + decoy.when(mock_engine_client.state.config.robot_type).then_return(robot_type) + + if robot_type == "OT-2 Standard": + decoy.when(subject.get_slot_definition(slot_name)).then_return( + cast(SlotDefV3, {"compatibleModuleTypes": []}) + ) + else: + decoy.when( + mock_engine_client.state.addressable_areas.state.deck_definition + ).then_return(deck_def) + decoy.when( + mock_engine_client.state.addressable_areas.get_cutout_id_by_deck_slot_name( + slot_name + ) + ).then_return("cutout" + slot_name.value) + + with pytest.raises( + ValueError, + match=f"Module Type {ModuleType.from_model(requested_model).value} does not have a related fixture ID.", + ): + subject.load_module( + model=requested_model, + deck_slot=slot_name, + configuration=None, + ) + + # APIv2.15 because we're expecting a fixed trash. @pytest.mark.parametrize("api_version", [APIVersion(2, 15)]) def test_load_mag_block( @@ -1366,6 +1450,14 @@ def test_load_mag_block( }, ) ) + decoy.when( + mock_engine_client.state.addressable_areas.state.deck_definition + ).then_return(ot3_standard_deck_def) + decoy.when( + mock_engine_client.state.addressable_areas.get_cutout_id_by_deck_slot_name( + DeckSlotName.SLOT_A2 + ) + ).then_return("cutout" + DeckSlotName.SLOT_A2.value) decoy.when( mock_engine_client.load_module( @@ -1450,12 +1542,14 @@ def test_load_module_thermocycler_with_no_location( decoy.when(mock_hw_mod.device_info).then_return({"serial": "xyz789"}) decoy.when(mock_sync_hardware_api.attached_modules).then_return([mock_hw_mod]) decoy.when(mock_engine_client.state.config.robot_type).then_return("OT-3 Standard") - decoy.when(subject.get_slot_definition(expected_slot)).then_return( - cast( - SlotDefV3, - {"compatibleModuleTypes": [ModuleType.from_model(requested_model)]}, + decoy.when( + mock_engine_client.state.addressable_areas.state.deck_definition + ).then_return(deck_def) + decoy.when( + mock_engine_client.state.addressable_areas.get_cutout_id_by_deck_slot_name( + expected_slot ) - ) + ).then_return("cutout" + expected_slot.value) decoy.when( mock_engine_client.load_module( diff --git a/api/tests/opentrons/protocol_engine/test_create_protocol_engine.py b/api/tests/opentrons/protocol_engine/test_create_protocol_engine.py index c175307160b..2f7a0cae441 100644 --- a/api/tests/opentrons/protocol_engine/test_create_protocol_engine.py +++ b/api/tests/opentrons/protocol_engine/test_create_protocol_engine.py @@ -4,6 +4,7 @@ from opentrons_shared_data.deck.dev_types import DeckDefinitionV5 from opentrons_shared_data.robot.dev_types import RobotType +from opentrons_shared_data.deck import load as load_deck from opentrons.calibration_storage.helpers import uri_from_details from opentrons.hardware_control import API as HardwareAPI @@ -18,6 +19,30 @@ from opentrons.protocol_engine.types import DeckSlotLocation, LoadedLabware from opentrons.types import DeckSlotName +from opentrons.protocols.api_support.deck_type import ( + STANDARD_OT2_DECK, + SHORT_TRASH_DECK, + STANDARD_OT3_DECK, +) + + +@pytest.fixture(scope="session") +def ot2_standard_deck_def() -> DeckDefinitionV5: + """Get the OT-2 standard deck definition.""" + return load_deck(STANDARD_OT2_DECK, 5) + + +@pytest.fixture(scope="session") +def ot2_short_trash_deck_def() -> DeckDefinitionV5: + """Get the OT-2 with short trash standard deck definition.""" + return load_deck(SHORT_TRASH_DECK, 5) + + +@pytest.fixture(scope="session") +def ot3_standard_deck_def() -> DeckDefinitionV5: + """Get the OT-2 standard deck definition.""" + return load_deck(STANDARD_OT3_DECK, 5) + @pytest.mark.parametrize( ( diff --git a/robot-server/robot_server/persistence/_migrations/v3_to_v4.py b/robot-server/robot_server/persistence/_migrations/v3_to_v4.py index 0f5cc0004cb..2590fa10b18 100644 --- a/robot-server/robot_server/persistence/_migrations/v3_to_v4.py +++ b/robot-server/robot_server/persistence/_migrations/v3_to_v4.py @@ -19,6 +19,6 @@ def migrate(self, source_dir: Path, dest_dir: Path) -> None: """Migrate the persistence directory from schema 3 to 4.""" for item in source_dir.iterdir(): if item.is_dir(): - shutil.copytree(src=item, dst=dest_dir) + shutil.copytree(src=item, dst=dest_dir / item.name) else: - shutil.copy(src=item, dst=dest_dir) + shutil.copy(src=item, dst=dest_dir / item.name) diff --git a/robot-server/tests/integration/http_api/persistence/test_reset.py b/robot-server/tests/integration/http_api/persistence/test_reset.py index 1eba97c5e46..ee0142cf828 100644 --- a/robot-server/tests/integration/http_api/persistence/test_reset.py +++ b/robot-server/tests/integration/http_api/persistence/test_reset.py @@ -40,9 +40,9 @@ async def _assert_reset_was_successful( all_files_and_directories = set(persistence_directory.glob("**/*")) expected_files_and_directories = { persistence_directory / "robot_server.db", - persistence_directory / "3", - persistence_directory / "3" / "protocols", - persistence_directory / "3" / "robot_server.db", + persistence_directory / "4", + persistence_directory / "4" / "protocols", + persistence_directory / "4" / "robot_server.db", } assert all_files_and_directories == expected_files_and_directories diff --git a/robot-server/tests/integration/http_api/protocols/test_persistence.py b/robot-server/tests/integration/http_api/protocols/test_persistence.py index d7957188222..616a9a185fc 100644 --- a/robot-server/tests/integration/http_api/protocols/test_persistence.py +++ b/robot-server/tests/integration/http_api/protocols/test_persistence.py @@ -132,10 +132,10 @@ async def test_protocol_labware_files_persist() -> None: assert restarted_protocol_detail == protocol_detail four_tuberack = Path( - f"{server.persistence_directory}/3/protocols/{protocol_id}/cpx_4_tuberack_100ul.json" + f"{server.persistence_directory}/4/protocols/{protocol_id}/cpx_4_tuberack_100ul.json" ) six_tuberack = Path( - f"{server.persistence_directory}/3/protocols/{protocol_id}/cpx_6_tuberack_100ul.json" + f"{server.persistence_directory}/4/protocols/{protocol_id}/cpx_6_tuberack_100ul.json" ) assert four_tuberack.is_file() assert six_tuberack.is_file() diff --git a/shared-data/deck/types/schemaV5.ts b/shared-data/deck/types/schemaV5.ts index 7e2bb027088..0efc782b8c1 100644 --- a/shared-data/deck/types/schemaV5.ts +++ b/shared-data/deck/types/schemaV5.ts @@ -119,7 +119,7 @@ export type WasteChuteCutoutFixtureId = | 'stagingAreaSlotWithWasteChuteRightAdapterCovered' | 'stagingAreaSlotWithWasteChuteRightAdapterNoCover' -export type FlexModuleCutoutFixtureId = +export type FlexModuleCutoutFixtureId = | 'heaterShakerModuleV1' | 'temperatureModuleV2' | 'magneticBlockModuleV1' @@ -135,6 +135,6 @@ export type CutoutFixtureId = | StagingAreaRightSlotFixtureId | TrashBinAdapterCutoutFixtureId | WasteChuteCutoutFixtureId - | FlexModuleCutoutFixtureId + | FlexModuleCutoutFixtureId | OT2SingleStandardSlot | OT2FixedTrashSlot diff --git a/shared-data/js/constants.ts b/shared-data/js/constants.ts index d5216e27184..949720e8985 100644 --- a/shared-data/js/constants.ts +++ b/shared-data/js/constants.ts @@ -1,4 +1,9 @@ -import type { CutoutFixtureId, CutoutId, AddressableAreaName, FlexModuleCutoutFixtureId } from '../deck' +import type { + CutoutFixtureId, + CutoutId, + AddressableAreaName, + FlexModuleCutoutFixtureId, +} from '../deck' import type { ModuleModel, ModuleType } from './types' // constants for dealing with robot coordinate system (eg in labwareTools) @@ -275,23 +280,40 @@ export const NINETY_SIX_CHANNEL_WASTE_CHUTE_ADDRESSABLE_AREA: '96ChannelWasteChu export const GRIPPER_WASTE_CHUTE_ADDRESSABLE_AREA: 'gripperWasteChute' = 'gripperWasteChute' -export const THERMOCYCLER_ADDRESSABLE_AREA: 'thermocyclerModule' = 'thermocyclerModule' -export const HEATERSHAKER_A1_ADDRESSABLE_AREA: 'heaterShakerA1' = 'heaterShakerA1' -export const HEATERSHAKER_B1_ADDRESSABLE_AREA: 'heaterShakerB1' = 'heaterShakerB1' -export const HEATERSHAKER_C1_ADDRESSABLE_AREA: 'heaterShakerC1' = 'heaterShakerC1' -export const HEATERSHAKER_D1_ADDRESSABLE_AREA: 'heaterShakerD1' = 'heaterShakerD1' -export const HEATERSHAKER_A3_ADDRESSABLE_AREA: 'heaterShakerA3' = 'heaterShakerA3' -export const HEATERSHAKER_B3_ADDRESSABLE_AREA: 'heaterShakerB3' = 'heaterShakerB3' -export const HEATERSHAKER_C3_ADDRESSABLE_AREA: 'heaterShakerC3' = 'heaterShakerC3' -export const HEATERSHAKER_D3_ADDRESSABLE_AREA: 'heaterShakerD3' = 'heaterShakerD3' -export const TEMPERATURE_MODULE_A1_ADDRESSABLE_AREA: 'temperatureModuleA1' = 'temperatureModuleA1' -export const TEMPERATURE_MODULE_B1_ADDRESSABLE_AREA: 'temperatureModuleB1' = 'temperatureModuleB1' -export const TEMPERATURE_MODULE_C1_ADDRESSABLE_AREA: 'temperatureModuleC1' = 'temperatureModuleC1' -export const TEMPERATURE_MODULE_D1_ADDRESSABLE_AREA: 'temperatureModuleD1' = 'temperatureModuleD1' -export const TEMPERATURE_MODULE_A3_ADDRESSABLE_AREA: 'temperatureModuleA3' = 'temperatureModuleA3' -export const TEMPERATURE_MODULE_B3_ADDRESSABLE_AREA: 'temperatureModuleB3' = 'temperatureModuleB3' -export const TEMPERATURE_MODULE_C3_ADDRESSABLE_AREA: 'temperatureModuleC3' = 'temperatureModuleC3' -export const TEMPERATURE_MODULE_D3_ADDRESSABLE_AREA: 'temperatureModuleD3' = 'temperatureModuleD3' +export const THERMOCYCLER_ADDRESSABLE_AREA: 'thermocyclerModule' = + 'thermocyclerModule' +export const HEATERSHAKER_A1_ADDRESSABLE_AREA: 'heaterShakerA1' = + 'heaterShakerA1' +export const HEATERSHAKER_B1_ADDRESSABLE_AREA: 'heaterShakerB1' = + 'heaterShakerB1' +export const HEATERSHAKER_C1_ADDRESSABLE_AREA: 'heaterShakerC1' = + 'heaterShakerC1' +export const HEATERSHAKER_D1_ADDRESSABLE_AREA: 'heaterShakerD1' = + 'heaterShakerD1' +export const HEATERSHAKER_A3_ADDRESSABLE_AREA: 'heaterShakerA3' = + 'heaterShakerA3' +export const HEATERSHAKER_B3_ADDRESSABLE_AREA: 'heaterShakerB3' = + 'heaterShakerB3' +export const HEATERSHAKER_C3_ADDRESSABLE_AREA: 'heaterShakerC3' = + 'heaterShakerC3' +export const HEATERSHAKER_D3_ADDRESSABLE_AREA: 'heaterShakerD3' = + 'heaterShakerD3' +export const TEMPERATURE_MODULE_A1_ADDRESSABLE_AREA: 'temperatureModuleA1' = + 'temperatureModuleA1' +export const TEMPERATURE_MODULE_B1_ADDRESSABLE_AREA: 'temperatureModuleB1' = + 'temperatureModuleB1' +export const TEMPERATURE_MODULE_C1_ADDRESSABLE_AREA: 'temperatureModuleC1' = + 'temperatureModuleC1' +export const TEMPERATURE_MODULE_D1_ADDRESSABLE_AREA: 'temperatureModuleD1' = + 'temperatureModuleD1' +export const TEMPERATURE_MODULE_A3_ADDRESSABLE_AREA: 'temperatureModuleA3' = + 'temperatureModuleA3' +export const TEMPERATURE_MODULE_B3_ADDRESSABLE_AREA: 'temperatureModuleB3' = + 'temperatureModuleB3' +export const TEMPERATURE_MODULE_C3_ADDRESSABLE_AREA: 'temperatureModuleC3' = + 'temperatureModuleC3' +export const TEMPERATURE_MODULE_D3_ADDRESSABLE_AREA: 'temperatureModuleD3' = + 'temperatureModuleD3' export const ADDRESSABLE_AREA_1: '1' = '1' export const ADDRESSABLE_AREA_2: '2' = '2' @@ -377,17 +399,27 @@ export const STAGING_AREA_SLOT_WITH_WASTE_CHUTE_RIGHT_ADAPTER_COVERED_FIXTURE: ' export const STAGING_AREA_SLOT_WITH_WASTE_CHUTE_RIGHT_ADAPTER_NO_COVER_FIXTURE: 'stagingAreaSlotWithWasteChuteRightAdapterNoCover' = 'stagingAreaSlotWithWasteChuteRightAdapterNoCover' -export const HEATERSHAKER_MODULE_V1_FIXTURE: 'heaterShakerModuleV1' = 'heaterShakerModuleV1' -export const TEMPERATURE_MODULE_V2_FIXTURE: 'temperatureModuleV2' = 'temperatureModuleV2' -export const MAGNETIC_BLOCK_V1_FIXTURE: 'magneticBlockModuleV1' = 'magneticBlockModuleV1' -export const THERMOCYCLER_V2_REAR_FIXTURE: 'thermocyclerModuleV2Rear' = 'thermocyclerModuleV2Rear' -export const THERMOCYCLER_V2_FRONT_FIXTURE: 'thermocyclerModuleV2Front' = 'thermocyclerModuleV2Front' - -export const MODULE_FIXTURES_BY_MODEL: {[moduleModel in ModuleModel]?: FlexModuleCutoutFixtureId[]} = { +export const HEATERSHAKER_MODULE_V1_FIXTURE: 'heaterShakerModuleV1' = + 'heaterShakerModuleV1' +export const TEMPERATURE_MODULE_V2_FIXTURE: 'temperatureModuleV2' = + 'temperatureModuleV2' +export const MAGNETIC_BLOCK_V1_FIXTURE: 'magneticBlockModuleV1' = + 'magneticBlockModuleV1' +export const THERMOCYCLER_V2_REAR_FIXTURE: 'thermocyclerModuleV2Rear' = + 'thermocyclerModuleV2Rear' +export const THERMOCYCLER_V2_FRONT_FIXTURE: 'thermocyclerModuleV2Front' = + 'thermocyclerModuleV2Front' + +export const MODULE_FIXTURES_BY_MODEL: { + [moduleModel in ModuleModel]?: FlexModuleCutoutFixtureId[] +} = { [HEATERSHAKER_MODULE_V1]: [HEATERSHAKER_MODULE_V1_FIXTURE], [TEMPERATURE_MODULE_V2]: [TEMPERATURE_MODULE_V2_FIXTURE], [MAGNETIC_BLOCK_V1]: [MAGNETIC_BLOCK_V1_FIXTURE], - [THERMOCYCLER_MODULE_V2]: [THERMOCYCLER_V2_REAR_FIXTURE, THERMOCYCLER_V2_FRONT_FIXTURE], + [THERMOCYCLER_MODULE_V2]: [ + THERMOCYCLER_V2_REAR_FIXTURE, + THERMOCYCLER_V2_FRONT_FIXTURE, + ], } export const SINGLE_SLOT_FIXTURES: CutoutFixtureId[] = [ diff --git a/shared-data/js/fixtures.ts b/shared-data/js/fixtures.ts index 44da5ccb56d..6fdac9a2710 100644 --- a/shared-data/js/fixtures.ts +++ b/shared-data/js/fixtures.ts @@ -40,8 +40,20 @@ import { THERMOCYCLER_V2_FRONT_FIXTURE, MODULE_FIXTURES_BY_MODEL, } from './constants' -import type { AddressableAreaName, CutoutFixtureId, CutoutId, FlexAddressableAreaName, FlexModuleCutoutFixtureId, OT2CutoutId } from '../deck' -import type { AddressableArea, CoordinateTuple, DeckDefinition, ModuleModel } from './types' +import type { + AddressableAreaName, + CutoutFixtureId, + CutoutId, + FlexAddressableAreaName, + FlexModuleCutoutFixtureId, + OT2CutoutId, +} from '../deck' +import type { + AddressableArea, + CoordinateTuple, + DeckDefinition, + ModuleModel, +} from './types' import type { LoadModuleCreateCommand, ModuleLocation } from '../command' import { getModuleDisplayName } from './modules' @@ -122,10 +134,10 @@ export function getPositionFromSlotId( const slotPosition: CoordinateTuple | null = cutoutPosition != null ? [ - cutoutPosition[0] + offsetFromCutoutFixture[0], - cutoutPosition[1] + offsetFromCutoutFixture[1], - cutoutPosition[2] + offsetFromCutoutFixture[2], - ] + cutoutPosition[0] + offsetFromCutoutFixture[0], + cutoutPosition[1] + offsetFromCutoutFixture[1], + cutoutPosition[2] + offsetFromCutoutFixture[2], + ] : null return slotPosition @@ -153,9 +165,11 @@ export function getCutoutIdFromModuleLocation( location: ModuleLocation, deckDef: DeckDefinition ): CutoutId | null { - return deckDef.locations.cutouts.find( - cutout => cutout.id.includes(location.slotName) - )?.id ?? null + return ( + deckDef.locations.cutouts.find(cutout => + cutout.id.includes(location.slotName) + )?.id ?? null + ) } export function getAddressableAreaNamesFromLoadedModule( @@ -164,13 +178,18 @@ export function getAddressableAreaNamesFromLoadedModule( ): AddressableAreaName[] { const moduleFixtureIds = getCutoutFixtureIdsForModuleModel(params.model) const cutoutId = getCutoutIdFromModuleLocation(params.location, deckDef) - return moduleFixtureIds.reduce((acc, cutoutFixtureId) => { - const cutoutFixture = deckDef.cutoutFixtures.find( - cf => cf.id === cutoutFixtureId - ) ?? null - const providedAddressableAreas = cutoutId != null ?cutoutFixture?.providesAddressableAreas[cutoutId] ?? [] : [] - return [...acc, ...providedAddressableAreas] - }, []) + return moduleFixtureIds.reduce( + (acc, cutoutFixtureId) => { + const cutoutFixture = + deckDef.cutoutFixtures.find(cf => cf.id === cutoutFixtureId) ?? null + const providedAddressableAreas = + cutoutId != null + ? cutoutFixture?.providesAddressableAreas[cutoutId] ?? [] + : [] + return [...acc, ...providedAddressableAreas] + }, + [] + ) } export function getFixtureDisplayName( @@ -204,7 +223,7 @@ export function getFixtureDisplayName( } } -const STANDARD_OT2_SLOTS: AddressableAreaName[]= [ +const STANDARD_OT2_SLOTS: AddressableAreaName[] = [ ADDRESSABLE_AREA_1, ADDRESSABLE_AREA_2, ADDRESSABLE_AREA_3, diff --git a/shared-data/js/helpers/getAddressableAreasInProtocol.ts b/shared-data/js/helpers/getAddressableAreasInProtocol.ts index 82367c77a5a..c96fcf3c6ec 100644 --- a/shared-data/js/helpers/getAddressableAreasInProtocol.ts +++ b/shared-data/js/helpers/getAddressableAreasInProtocol.ts @@ -1,5 +1,8 @@ import { MOVABLE_TRASH_A3_ADDRESSABLE_AREA } from '../constants' -import { getAddressableAreaNamesFromLoadedModule, getAddressableAreaFromSlotId } from '../fixtures' +import { + getAddressableAreaNamesFromLoadedModule, + getAddressableAreaFromSlotId, +} from '../fixtures' import type { AddressableAreaName } from '../../deck' import type { ProtocolAnalysisOutput } from '../../protocol' import type { CompletedProtocolAnalysis, DeckDefinition } from '../types' diff --git a/shared-data/python/opentrons_shared_data/deck/dev_types.py b/shared-data/python/opentrons_shared_data/deck/dev_types.py index f168c232fb4..e8d231b2f96 100644 --- a/shared-data/python/opentrons_shared_data/deck/dev_types.py +++ b/shared-data/python/opentrons_shared_data/deck/dev_types.py @@ -4,7 +4,7 @@ This should only be imported if typing.TYPE_CHECKING is True """ -from typing import Any, Dict, List, NewType, Union, Optional +from typing import Any, Dict, List, NewType, Union from typing_extensions import Literal, TypedDict from ..module.dev_types import ModuleType