From f16f6a0d2900be1511e3ff37ae7da698b375cb9b Mon Sep 17 00:00:00 2001 From: Jethary Alcid <66035149+jerader@users.noreply.github.com> Date: Wed, 5 Feb 2025 15:05:26 -0500 Subject: [PATCH] =?UTF-8?q?feat(protocol-designer):=20add=20designerApplic?= =?UTF-8?q?ation=20labware,=20pipettes,=20m=E2=80=A6=20(#17432)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit …odules keys closes AUTH-1407 This PR introduces `designerApplication` top level keys for `modules`, `labware`, and `pipettes`. Then when you import a protocol, the migration will grab that info from the load commands and the `load-file` reducer will use that info to populate the `labwareEntities`, `pipetteEntities`, and `moduleEntities`. Additionally, this pr deprecates the `defaultValues` key that was not in use at all --- .../protocol/8/doItAllV3MigratedToV8.json | 30 ++++-- .../protocol/8/doItAllV4MigratedToV8.json | 37 +++++-- .../protocol/8/doItAllV7MigratedToV8.json | 54 ++++++++-- .../fixtures/protocol/8/doItAllV8.json | 41 +++++-- .../protocol/8/example_1_1_0MigratedToV8.json | 33 ++++-- .../fixtures/protocol/8/mix_8_0_0.json | 26 +++-- .../8/newAdvancedSettingsAndMultiTemp.json | 41 +++++-- .../8/ninetySixChannelFullAndColumn.json | 34 ++++-- .../8/thermocyclerOnOt2V7MigratedToV8.json | 26 +++-- .../src/file-data/__tests__/utils.test.tsx | 82 ++++++++++++++ .../src/file-data/selectors/fileCreator.ts | 43 ++------ .../src/file-data/selectors/utils.ts | 47 +++++++- protocol-designer/src/file-types.ts | 41 ++++--- .../src/load-file/migration/8_5_0.ts | 11 +- .../getEquipmentLoadInfoFromCommands.test.tsx | 67 ++++++++++++ .../utils/getEquipmentLoadInfoFromCommands.ts | 101 ++++++++++++++++++ .../src/step-forms/reducers/index.ts | 98 +++++------------ 17 files changed, 613 insertions(+), 199 deletions(-) create mode 100644 protocol-designer/src/file-data/__tests__/utils.test.tsx create mode 100644 protocol-designer/src/load-file/migration/utils/__tests__/getEquipmentLoadInfoFromCommands.test.tsx create mode 100644 protocol-designer/src/load-file/migration/utils/getEquipmentLoadInfoFromCommands.ts diff --git a/protocol-designer/fixtures/protocol/8/doItAllV3MigratedToV8.json b/protocol-designer/fixtures/protocol/8/doItAllV3MigratedToV8.json index bfe4767cdff..48c933990a4 100644 --- a/protocol-designer/fixtures/protocol/8/doItAllV3MigratedToV8.json +++ b/protocol-designer/fixtures/protocol/8/doItAllV3MigratedToV8.json @@ -15,13 +15,7 @@ "name": "opentrons/protocol-designer", "version": "8.5.0", "data": { - "_internalAppBuildDate": "Tue, 04 Feb 2025 15:16:30 GMT", - "defaultValues": { - "aspirate_mmFromBottom": 1, - "dispense_mmFromBottom": 1, - "touchTip_mmFromTop": -1, - "blowout_mmFromTop": 0 - }, + "_internalAppBuildDate": "Wed, 29 Jan 2025 13:23:47 GMT", "pipetteTiprackAssignments": { "0b3f2210-75c7-11ea-b42f-4b64e50f43e5": [ "opentrons/opentrons_96_tiprack_300ul/1" @@ -266,7 +260,27 @@ "3961e4c0-75c7-11ea-b42f-4b64e50f43e5", "54dc3200-75c7-11ea-b42f-4b64e50f43e5", "a4cee9a0-75dc-11ea-b42f-4b64e50f43e5" - ] + ], + "pipettes": { + "0b3f2210-75c7-11ea-b42f-4b64e50f43e5": { + "pipetteName": "p300_single_gen2" + } + }, + "modules": {}, + "labware": { + "0b44c760-75c7-11ea-b42f-4b64e50f43e5:opentrons/opentrons_96_tiprack_300ul/1": { + "displayName": "Opentrons 96 Tip Rack 300 µL", + "labwareDefURI": "opentrons/opentrons_96_tiprack_300ul/1" + }, + "1e610d40-75c7-11ea-b42f-4b64e50f43e5:opentrons/nest_96_wellplate_100ul_pcr_full_skirt/1": { + "displayName": "NEST 96 Well Plate 100 µL PCR Full Skirt", + "labwareDefURI": "opentrons/nest_96_wellplate_100ul_pcr_full_skirt/1" + }, + "21ed8f60-75c7-11ea-b42f-4b64e50f43e5:opentrons/opentrons_24_aluminumblock_generic_2ml_screwcap/1": { + "displayName": "Opentrons 24 Well Aluminum Block with Generic 2 mL Screwcap", + "labwareDefURI": "opentrons/opentrons_24_aluminumblock_generic_2ml_screwcap/1" + } + } } }, "robot": { diff --git a/protocol-designer/fixtures/protocol/8/doItAllV4MigratedToV8.json b/protocol-designer/fixtures/protocol/8/doItAllV4MigratedToV8.json index 5ef201e8e50..ea1d1cb31f3 100644 --- a/protocol-designer/fixtures/protocol/8/doItAllV4MigratedToV8.json +++ b/protocol-designer/fixtures/protocol/8/doItAllV4MigratedToV8.json @@ -15,13 +15,7 @@ "name": "opentrons/protocol-designer", "version": "8.5.0", "data": { - "_internalAppBuildDate": "Tue, 04 Feb 2025 15:16:30 GMT", - "defaultValues": { - "aspirate_mmFromBottom": 1, - "dispense_mmFromBottom": 1, - "touchTip_mmFromTop": -1, - "blowout_mmFromTop": 0 - }, + "_internalAppBuildDate": "Wed, 29 Jan 2025 13:23:47 GMT", "pipetteTiprackAssignments": { "0b3f2210-75c7-11ea-b42f-4b64e50f43e5": [ "opentrons/opentrons_96_tiprack_300ul/1" @@ -288,7 +282,34 @@ "4f4057e0-75c7-11ea-b42f-4b64e50f43e5", "54dc3200-75c7-11ea-b42f-4b64e50f43e5", "80c00130-75c7-11ea-b42f-4b64e50f43e5" - ] + ], + "pipettes": { + "0b3f2210-75c7-11ea-b42f-4b64e50f43e5": { + "pipetteName": "p300_single_gen2" + } + }, + "modules": { + "0b419310-75c7-11ea-b42f-4b64e50f43e5:magneticModuleType": { + "model": "magneticModuleV2" + }, + "0b4319b0-75c7-11ea-b42f-4b64e50f43e5:temperatureModuleType": { + "model": "temperatureModuleV2" + } + }, + "labware": { + "0b44c760-75c7-11ea-b42f-4b64e50f43e5:opentrons/opentrons_96_tiprack_300ul/1": { + "displayName": "Opentrons 96 Tip Rack 300 µL", + "labwareDefURI": "opentrons/opentrons_96_tiprack_300ul/1" + }, + "1e610d40-75c7-11ea-b42f-4b64e50f43e5:opentrons/nest_96_wellplate_100ul_pcr_full_skirt/1": { + "displayName": "NEST 96 Well Plate 100 µL PCR Full Skirt", + "labwareDefURI": "opentrons/nest_96_wellplate_100ul_pcr_full_skirt/1" + }, + "21ed8f60-75c7-11ea-b42f-4b64e50f43e5:opentrons/opentrons_24_aluminumblock_generic_2ml_screwcap/1": { + "displayName": "Opentrons 24 Well Aluminum Block with Generic 2 mL Screwcap", + "labwareDefURI": "opentrons/opentrons_24_aluminumblock_generic_2ml_screwcap/1" + } + } } }, "robot": { diff --git a/protocol-designer/fixtures/protocol/8/doItAllV7MigratedToV8.json b/protocol-designer/fixtures/protocol/8/doItAllV7MigratedToV8.json index 17257c9b64d..5ceb1bedabb 100644 --- a/protocol-designer/fixtures/protocol/8/doItAllV7MigratedToV8.json +++ b/protocol-designer/fixtures/protocol/8/doItAllV7MigratedToV8.json @@ -15,13 +15,7 @@ "name": "opentrons/protocol-designer", "version": "8.5.0", "data": { - "_internalAppBuildDate": "Tue, 04 Feb 2025 15:16:30 GMT", - "defaultValues": { - "aspirate_mmFromBottom": 1, - "dispense_mmFromBottom": 1, - "touchTip_mmFromTop": -1, - "blowout_mmFromTop": 0 - }, + "_internalAppBuildDate": "Wed, 29 Jan 2025 13:23:47 GMT", "pipetteTiprackAssignments": { "2e7c6344-58ab-465c-b542-489883cb63fe": [ "opentrons/opentrons_flex_96_filtertiprack_50ul/1" @@ -409,7 +403,51 @@ "7747287c-abea-4855-843e-d61b272124b2", "dcc6a6c7-2db8-417b-a1aa-3927abccfadd", "2f862881-7ce3-4d20-b0ef-53c8244f6ef3" - ] + ], + "pipettes": { + "2e7c6344-58ab-465c-b542-489883cb63fe": { + "pipetteName": "p1000_single_flex" + }, + "6d1e53c3-2db3-451b-ad60-3fe13781a193": { + "pipetteName": "p50_multi_flex" + } + }, + "modules": { + "1be16305-74e7-4bdb-9737-61ec726d2b44:magneticBlockType": { + "model": "magneticBlockV1" + }, + "c19dffa3-cb34-4702-bcf6-dcea786257d1:heaterShakerModuleType": { + "model": "heaterShakerModuleV1" + }, + "ef44ad7f-0fd9-46d6-8bc0-c70785644cc8:temperatureModuleType": { + "model": "temperatureModuleV2" + }, + "627b7a27-5bb7-46de-a530-67af45652e3b:thermocyclerModuleType": { + "model": "thermocyclerModuleV2" + } + }, + "labware": { + "d95bb3be-b453-457c-a947-bd03dc8e56b9:opentrons/opentrons_96_flat_bottom_adapter/1": { + "displayName": "Opentrons 96 Flat Bottom Heater-Shaker Adapter", + "labwareDefURI": "opentrons/opentrons_96_flat_bottom_adapter/1" + }, + "23ed35de-5bfd-4bb0-8f54-da99a2804ed9:opentrons/opentrons_flex_96_filtertiprack_50ul/1": { + "displayName": "Opentrons Flex 96 Filter Tip Rack 50 µL", + "labwareDefURI": "opentrons/opentrons_flex_96_filtertiprack_50ul/1" + }, + "fcba73e7-b88e-438e-963e-f8b9a5de0983:opentrons/nest_96_wellplate_100ul_pcr_full_skirt/2": { + "displayName": "NEST 96 Well Plate 100 µL PCR Full Skirt", + "labwareDefURI": "opentrons/nest_96_wellplate_100ul_pcr_full_skirt/2" + }, + "a793a135-06aa-4ed6-a1d3-c176c7810afa:opentrons/opentrons_24_aluminumblock_nest_1.5ml_snapcap/1": { + "displayName": "Opentrons 24 Well Aluminum Block with NEST 1.5 mL Snapcap", + "labwareDefURI": "opentrons/opentrons_24_aluminumblock_nest_1.5ml_snapcap/1" + }, + "239ceac8-23ec-4900-810a-70aeef880273:opentrons/nest_96_wellplate_200ul_flat/2": { + "displayName": "NEST 96 Well Plate 200 µL Flat", + "labwareDefURI": "opentrons/nest_96_wellplate_200ul_flat/2" + } + } } }, "robot": { diff --git a/protocol-designer/fixtures/protocol/8/doItAllV8.json b/protocol-designer/fixtures/protocol/8/doItAllV8.json index aaece68eb3a..7015a8a0c94 100644 --- a/protocol-designer/fixtures/protocol/8/doItAllV8.json +++ b/protocol-designer/fixtures/protocol/8/doItAllV8.json @@ -15,13 +15,7 @@ "name": "opentrons/protocol-designer", "version": "8.5.0", "data": { - "_internalAppBuildDate": "Wed, 05 Feb 2025 18:32:26 GMT", - "defaultValues": { - "aspirate_mmFromBottom": 1, - "dispense_mmFromBottom": 1, - "touchTip_mmFromTop": -1, - "blowout_mmFromTop": 0 - }, + "_internalAppBuildDate": "Wed, 29 Jan 2025 13:23:47 GMT", "pipetteTiprackAssignments": { "9fcd50d9-92b2-45ac-acf1-e2cf773feffc": [ "opentrons/opentrons_flex_96_tiprack_1000ul/1" @@ -358,7 +352,38 @@ "2b8f84e2-b079-41e8-a66e-ff8d9c5dfe1d", "07dd4472-3ea4-475c-8fd3-18819519b401", "ed84f11e-db82-4039-9e04-e619b03af42f" - ] + ], + "pipettes": { + "9fcd50d9-92b2-45ac-acf1-e2cf773feffc": { + "pipetteName": "p1000_single_flex" + } + }, + "modules": { + "23347241-80bb-4a7e-9c91-5d9727a9e483:heaterShakerModuleType": { + "model": "heaterShakerModuleV1" + }, + "fd6da9f1-d63b-414b-929e-c646b64790e9:thermocyclerModuleType": { + "model": "thermocyclerModuleV2" + } + }, + "labware": { + "7c4d59fa-0e50-442f-adce-9e4b0c7f0b88:opentrons/opentrons_96_pcr_adapter/1": { + "displayName": "Opentrons 96 PCR Heater-Shaker Adapter", + "labwareDefURI": "opentrons/opentrons_96_pcr_adapter/1" + }, + "f2d371ea-5146-4c89-8200-9c056a7f321a:opentrons/opentrons_flex_96_tiprack_1000ul/1": { + "displayName": "Opentrons Flex 96 Tip Rack 1000 µL", + "labwareDefURI": "opentrons/opentrons_flex_96_tiprack_1000ul/1" + }, + "54370838-4fca-4a14-b88a-7840e4903649:opentrons/opentrons_96_wellplate_200ul_pcr_full_skirt/2": { + "displayName": "Opentrons Tough 96 Well Plate 200 µL PCR Full Skirt", + "labwareDefURI": "opentrons/opentrons_96_wellplate_200ul_pcr_full_skirt/2" + }, + "8bacda22-9e05-45e8-bef4-cc04414a204f:opentrons/axygen_1_reservoir_90ml/1": { + "displayName": "Axygen 1 Well Reservoir 90 mL", + "labwareDefURI": "opentrons/axygen_1_reservoir_90ml/1" + } + } } }, "robot": { diff --git a/protocol-designer/fixtures/protocol/8/example_1_1_0MigratedToV8.json b/protocol-designer/fixtures/protocol/8/example_1_1_0MigratedToV8.json index 7e0b8eeabff..dde560281b0 100644 --- a/protocol-designer/fixtures/protocol/8/example_1_1_0MigratedToV8.json +++ b/protocol-designer/fixtures/protocol/8/example_1_1_0MigratedToV8.json @@ -15,13 +15,7 @@ "name": "opentrons/protocol-designer", "version": "8.5.0", "data": { - "_internalAppBuildDate": "Tue, 04 Feb 2025 15:16:30 GMT", - "defaultValues": { - "aspirate_mmFromBottom": 1, - "dispense_mmFromBottom": 1, - "touchTip_mmFromTop": -1, - "blowout_mmFromTop": 0 - }, + "_internalAppBuildDate": "Thu, 30 Jan 2025 17:05:24 GMT", "pipetteTiprackAssignments": { "c6f45030-92a5-11e9-ac62-1b173f839d9e": [ "opentrons/opentrons_96_tiprack_10ul/1" @@ -235,7 +229,30 @@ "e7d36200-92a5-11e9-ac62-1b173f839d9e", "18113c80-92a6-11e9-ac62-1b173f839d9e", "2e622080-92a6-11e9-ac62-1b173f839d9e" - ] + ], + "pipettes": { + "c6f45030-92a5-11e9-ac62-1b173f839d9e": { + "pipetteName": "p10_single" + }, + "c6f47740-92a5-11e9-ac62-1b173f839d9e": { + "pipetteName": "p50_single" + } + }, + "modules": {}, + "labware": { + "c6f4ec70-92a5-11e9-ac62-1b173f839d9e:tiprack-10ul:opentrons/opentrons_96_tiprack_10ul/1": { + "displayName": "tiprack 10ul (1)", + "labwareDefURI": "opentrons/opentrons_96_tiprack_10ul/1" + }, + "c6f51380-92a5-11e9-ac62-1b173f839d9e:tiprack-200ul:opentrons/tipone_96_tiprack_200ul/1": { + "displayName": "tiprack 200ul (1)", + "labwareDefURI": "opentrons/tipone_96_tiprack_200ul/1" + }, + "dafd4000-92a5-11e9-ac62-1b173f839d9e:96-deep-well:opentrons/usascientific_96_wellplate_2.4ml_deep/1": { + "displayName": "96 deep well (1)", + "labwareDefURI": "opentrons/usascientific_96_wellplate_2.4ml_deep/1" + } + } } }, "robot": { diff --git a/protocol-designer/fixtures/protocol/8/mix_8_0_0.json b/protocol-designer/fixtures/protocol/8/mix_8_0_0.json index b3f37e7e9b5..b55b9b94478 100644 --- a/protocol-designer/fixtures/protocol/8/mix_8_0_0.json +++ b/protocol-designer/fixtures/protocol/8/mix_8_0_0.json @@ -13,15 +13,9 @@ }, "designerApplication": { "name": "opentrons/protocol-designer", - "version": "8.2.0", + "version": "8.5.0", "data": { "_internalAppBuildDate": "Wed, 01 May 2024 13:32:34 GMT", - "defaultValues": { - "aspirate_mmFromBottom": 1, - "dispense_mmFromBottom": 1, - "touchTip_mmFromTop": -1, - "blowout_mmFromTop": 0 - }, "pipetteTiprackAssignments": { "pipetteId": ["opentrons/opentrons_96_tiprack_10ul/1"] }, @@ -91,7 +85,23 @@ "orderedStepIds": [ "f59ea8e0-fc3a-11ea-8809-e959e7d61d96", "fc4dc7c0-fc3a-11ea-8809-e959e7d61d96" - ] + ], + "pipettes": { + "pipetteId": { + "pipetteName": "p20_single_gen2" + } + }, + "modules": {}, + "labware": { + "f1c677c0-fc3a-11ea-8809-e959e7d61d96:opentrons/opentrons_96_tiprack_10ul/1": { + "displayName": "Opentrons OT-2 96 Tip Rack 10 µL", + "labwareDefURI": "opentrons/opentrons_96_tiprack_10ul/1" + }, + "fe572c50-fc3a-11ea-8809-e959e7d61d96:opentrons/biorad_96_wellplate_200ul_pcr/1": { + "displayName": "Bio-Rad 96 Well Plate 200 µL PCR", + "labwareDefURI": "opentrons/biorad_96_wellplate_200ul_pcr/1" + } + } } }, "robot": { "model": "OT-2 Standard", "deckId": "ot2_standard" }, diff --git a/protocol-designer/fixtures/protocol/8/newAdvancedSettingsAndMultiTemp.json b/protocol-designer/fixtures/protocol/8/newAdvancedSettingsAndMultiTemp.json index eeb2cd9765c..e41bc40b181 100644 --- a/protocol-designer/fixtures/protocol/8/newAdvancedSettingsAndMultiTemp.json +++ b/protocol-designer/fixtures/protocol/8/newAdvancedSettingsAndMultiTemp.json @@ -15,13 +15,7 @@ "name": "opentrons/protocol-designer", "version": "8.5.0", "data": { - "_internalAppBuildDate": "Tue, 04 Feb 2025 15:16:30 GMT", - "defaultValues": { - "aspirate_mmFromBottom": 1, - "dispense_mmFromBottom": 1, - "touchTip_mmFromTop": -1, - "blowout_mmFromTop": 0 - }, + "_internalAppBuildDate": "Wed, 29 Jan 2025 13:23:47 GMT", "pipetteTiprackAssignments": { "21087f15-4c03-4587-8a2b-1ba0b5a501a0": [ "opentrons/opentrons_flex_96_tiprack_50ul/1" @@ -186,7 +180,38 @@ "c72b4af9-7488-4109-8221-15a5433f4fd8", "ffb0d1ff-8146-409c-9248-2065a3b27c4d", "eab2ec89-6d11-4246-ae91-d451cb3a5b1d" - ] + ], + "pipettes": { + "21087f15-4c03-4587-8a2b-1ba0b5a501a0": { + "pipetteName": "p50_single_flex" + } + }, + "modules": { + "d6966555-6c0e-45e0-8056-428d7c486401:temperatureModuleType": { + "model": "temperatureModuleV2" + }, + "b9c56153-9026-42d1-8113-949e15254571:temperatureModuleType": { + "model": "temperatureModuleV2" + } + }, + "labware": { + "32b596f6-79bb-4ad8-a34a-c44620fdb68f:opentrons/opentrons_96_well_aluminum_block/1": { + "displayName": "Opentrons 96 Well Aluminum Block", + "labwareDefURI": "opentrons/opentrons_96_well_aluminum_block/1" + }, + "0d39213c-49c2-4170-bf19-4c09e1b72aca:opentrons/opentrons_flex_96_tiprack_50ul/1": { + "displayName": "Opentrons Flex 96 Tip Rack 50 µL", + "labwareDefURI": "opentrons/opentrons_flex_96_tiprack_50ul/1" + }, + "c3c4e3fd-069f-4f3d-9b70-016a20f36de7:opentrons/opentrons_24_aluminumblock_nest_1.5ml_screwcap/1": { + "displayName": "Opentrons 24 Well Aluminum Block with NEST 1.5 mL Screwcap", + "labwareDefURI": "opentrons/opentrons_24_aluminumblock_nest_1.5ml_screwcap/1" + }, + "c0093e5f-3f7d-4cbf-aa17-d88394108501:opentrons/nest_96_wellplate_100ul_pcr_full_skirt/2": { + "displayName": "NEST 96 Well Plate 100 µL PCR Full Skirt", + "labwareDefURI": "opentrons/nest_96_wellplate_100ul_pcr_full_skirt/2" + } + } } }, "robot": { diff --git a/protocol-designer/fixtures/protocol/8/ninetySixChannelFullAndColumn.json b/protocol-designer/fixtures/protocol/8/ninetySixChannelFullAndColumn.json index f38b1eb287b..763f62e6c27 100644 --- a/protocol-designer/fixtures/protocol/8/ninetySixChannelFullAndColumn.json +++ b/protocol-designer/fixtures/protocol/8/ninetySixChannelFullAndColumn.json @@ -15,13 +15,7 @@ "name": "opentrons/protocol-designer", "version": "8.5.0", "data": { - "_internalAppBuildDate": "Tue, 04 Feb 2025 15:16:30 GMT", - "defaultValues": { - "aspirate_mmFromBottom": 1, - "dispense_mmFromBottom": 1, - "touchTip_mmFromTop": -1, - "blowout_mmFromTop": 0 - }, + "_internalAppBuildDate": "Wed, 29 Jan 2025 13:23:47 GMT", "pipetteTiprackAssignments": { "de7da440-95ec-43e8-8723-851321fbd6f9": [ "opentrons/opentrons_flex_96_tiprack_50ul/1" @@ -184,7 +178,31 @@ "orderedStepIds": [ "83a095fa-b649-4105-99d4-177f1a3f363a", "f5ea3139-1585-4848-9d5f-832eb88c99ca" - ] + ], + "pipettes": { + "de7da440-95ec-43e8-8723-851321fbd6f9": { + "pipetteName": "p1000_96" + } + }, + "modules": {}, + "labware": { + "ec850fd3-cf7c-44c5-b358-fba3a30315c9:opentrons/opentrons_flex_96_tiprack_adapter/1": { + "displayName": "Opentrons Flex 96 Tip Rack Adapter", + "labwareDefURI": "opentrons/opentrons_flex_96_tiprack_adapter/1" + }, + "75aa666f-98d8-4af9-908e-963ced428580:opentrons/opentrons_flex_96_tiprack_50ul/1": { + "displayName": "Opentrons Flex 96 Tip Rack 50 µL", + "labwareDefURI": "opentrons/opentrons_flex_96_tiprack_50ul/1" + }, + "fe1942b1-1b75-4d3a-9c12-d23004958a12:opentrons/biorad_96_wellplate_200ul_pcr/2": { + "displayName": "Bio-Rad 96 Well Plate 200 µL PCR", + "labwareDefURI": "opentrons/biorad_96_wellplate_200ul_pcr/2" + }, + "9bd16b50-4ae9-4cfd-8583-3378087e6a6c:opentrons/opentrons_flex_96_tiprack_50ul/1": { + "displayName": "Opentrons Flex 96 Tip Rack 50 µL", + "labwareDefURI": "opentrons/opentrons_flex_96_tiprack_50ul/1" + } + } } }, "robot": { diff --git a/protocol-designer/fixtures/protocol/8/thermocyclerOnOt2V7MigratedToV8.json b/protocol-designer/fixtures/protocol/8/thermocyclerOnOt2V7MigratedToV8.json index d08d93857e8..0fdb37a057e 100644 --- a/protocol-designer/fixtures/protocol/8/thermocyclerOnOt2V7MigratedToV8.json +++ b/protocol-designer/fixtures/protocol/8/thermocyclerOnOt2V7MigratedToV8.json @@ -15,13 +15,7 @@ "name": "opentrons/protocol-designer", "version": "8.5.0", "data": { - "_internalAppBuildDate": "Tue, 04 Feb 2025 15:40:12 GMT", - "defaultValues": { - "aspirate_mmFromBottom": 1, - "dispense_mmFromBottom": 1, - "touchTip_mmFromTop": -1, - "blowout_mmFromTop": 0 - }, + "_internalAppBuildDate": "Wed, 29 Jan 2025 13:23:47 GMT", "pipetteTiprackAssignments": { "733ba018-3550-476c-9fa3-0b5259d1a1d6": [ "opentrons/opentrons_96_tiprack_20ul/1" @@ -187,7 +181,23 @@ "orderedStepIds": [ "f07a86fc-8373-427b-b159-89ec8d20b9a6", "72b79c80-00e4-48ce-b42a-35b0ba17664c" - ] + ], + "pipettes": { + "733ba018-3550-476c-9fa3-0b5259d1a1d6": { + "pipetteName": "p20_single_gen2" + } + }, + "modules": { + "82858229-5c25-46cc-87d4-35ab318c18ce:thermocyclerModuleType": { + "model": "thermocyclerModuleV1" + } + }, + "labware": { + "ac928a51-a248-4304-be43-e9cb19c34fa9:opentrons/nest_96_wellplate_100ul_pcr_full_skirt/2": { + "displayName": "NEST 96 Well Plate 100 µL PCR Full Skirt", + "labwareDefURI": "opentrons/nest_96_wellplate_100ul_pcr_full_skirt/2" + } + } } }, "robot": { diff --git a/protocol-designer/src/file-data/__tests__/utils.test.tsx b/protocol-designer/src/file-data/__tests__/utils.test.tsx new file mode 100644 index 00000000000..34d08ca6f4b --- /dev/null +++ b/protocol-designer/src/file-data/__tests__/utils.test.tsx @@ -0,0 +1,82 @@ +import { describe, it, expect } from 'vitest' +import { + fixture96Plate, + fixtureP1000SingleV2Specs, +} from '@opentrons/shared-data' +import { + getLabwareLoadInfo, + getModulesLoadInfo, + getPipettesLoadInfo, +} from '../selectors/utils' +import type { LabwareDefinition2, PipetteName } from '@opentrons/shared-data' + +describe('getPipettesLoadInfo', () => { + it('returns pipettes from pipette entities', () => { + const pipId = '1' + const results = { + [pipId]: { + pipetteName: fixtureP1000SingleV2Specs.displayName, + }, + } + expect( + getPipettesLoadInfo({ + pipId: { + spec: fixtureP1000SingleV2Specs, + tiprackLabwareDef: [], + name: fixtureP1000SingleV2Specs.displayName as PipetteName, + id: pipId, + tiprackDefURI: [], + }, + }) + ).toEqual(results) + }) +}) + +describe('getModuleLoadInfo', () => { + it('returns modules from module entities', () => { + const moduleId = '1' + const results = { + [moduleId]: { + model: 'magneticModuleV2', + }, + } + expect( + getModulesLoadInfo({ + moduleId: { + id: moduleId, + model: 'magneticModuleV2', + type: 'magneticModuleType', + }, + }) + ).toEqual(results) + }) +}) + +describe('getLabwareLoadInfo', () => { + it('returns labwares from labware entities', () => { + const labwareId = '1' + const uri = 'mockUri' + const results = { + [labwareId]: { + displayName: 'nick name', + labwareDefURI: uri, + }, + } + const labwareNicknamesById: Record = { + [labwareId]: 'nick name', + } + + expect( + getLabwareLoadInfo( + { + labwareId: { + id: labwareId, + labwareDefURI: uri, + def: fixture96Plate as LabwareDefinition2, + }, + }, + labwareNicknamesById + ) + ).toEqual(results) + }) +}) diff --git a/protocol-designer/src/file-data/selectors/fileCreator.ts b/protocol-designer/src/file-data/selectors/fileCreator.ts index d63501dbc1e..23d78185b11 100644 --- a/protocol-designer/src/file-data/selectors/fileCreator.ts +++ b/protocol-designer/src/file-data/selectors/fileCreator.ts @@ -17,22 +17,21 @@ import { selectors as ingredSelectors } from '../../labware-ingred/selectors' import { selectors as stepFormSelectors } from '../../step-forms' import { selectors as uiLabwareSelectors } from '../../ui/labware' import { swatchColors } from '../../organisms/DefineLiquidsModal/swatchColors' -import { - DEFAULT_MM_TOUCH_TIP_OFFSET_FROM_TOP, - DEFAULT_MM_BLOWOUT_OFFSET_FROM_TOP, - DEFAULT_MM_OFFSET_FROM_BOTTOM, -} from '../../constants' import { getStepGroups } from '../../step-forms/selectors' import { getFileMetadata, getRobotType } from './fileFields' import { getInitialRobotState, getRobotStateTimeline } from './commands' -import { getLoadCommands } from './utils' +import { + getLabwareLoadInfo, + getLoadCommands, + getModulesLoadInfo, + getPipettesLoadInfo, +} from './utils' import type { SecondOrderCommandAnnotation } from '@opentrons/shared-data/commandAnnotation/types' import type { PipetteEntity, LabwareEntities, PipetteEntities, - LiquidEntities, } from '@opentrons/step-generation' import type { CommandAnnotationV1Mixin, @@ -45,23 +44,9 @@ import type { ProtocolBase, ProtocolFile, } from '@opentrons/shared-data' -import type { DismissedWarningState } from '../../dismiss/reducers' import type { LabwareDefByDefURI } from '../../labware-defs' import type { Selector } from '../../types' - -// DesignerApplication type for version 8_5 -export interface DesignerApplicationDataV8_5 { - ingredients: LiquidEntities - ingredLocations: { - [labwareId: string]: { - [wellName: string]: { [liquidId: string]: { volume: number } } - } - } - savedStepForms: Record - orderedStepIds: string[] - pipetteTiprackAssignments: Record - dismissedWarnings: DismissedWarningState -} +import type { PDMetadata } from '../../file-types' // TODO: BC: 2018-02-21 uncomment this assert, causes test failures // console.assert(!isEmpty(process.env.OT_PD_VERSION), 'Could not find application version!') @@ -159,15 +144,6 @@ export const createFile: Selector = createSelector( version: applicationVersion, data: { _internalAppBuildDate, - defaultValues: { - // TODO: Ian 2019-06-13 load these into redux and always get them from redux, not constants.js - // This `defaultValues` key is not yet read by anything, but is populated here for auditability - // and so that later we can do #3587 without a PD migration - aspirate_mmFromBottom: DEFAULT_MM_OFFSET_FROM_BOTTOM, - dispense_mmFromBottom: DEFAULT_MM_OFFSET_FROM_BOTTOM, - touchTip_mmFromTop: DEFAULT_MM_TOUCH_TIP_OFFSET_FROM_TOP, - blowout_mmFromTop: DEFAULT_MM_BLOWOUT_OFFSET_FROM_TOP, - }, pipetteTiprackAssignments: mapValues( pipetteEntities, (p: typeof pipetteEntities[keyof typeof pipetteEntities]): string[] => @@ -178,6 +154,9 @@ export const createFile: Selector = createSelector( ingredLocations, savedStepForms, orderedStepIds: savedOrderedStepIds, + pipettes: getPipettesLoadInfo(pipetteEntities), + modules: getModulesLoadInfo(moduleEntities), + labware: getLabwareLoadInfo(labwareEntities, labwareNicknamesById), }, } @@ -268,7 +247,7 @@ export const createFile: Selector = createSelector( commandAnnotations, } - const protocolBase: ProtocolBase = { + const protocolBase: ProtocolBase = { $otSharedSchema: '#/protocol/schemas/8', schemaVersion: 8, metadata: { diff --git a/protocol-designer/src/file-data/selectors/utils.ts b/protocol-designer/src/file-data/selectors/utils.ts index 8d852731fad..c8f6e3ff1f2 100644 --- a/protocol-designer/src/file-data/selectors/utils.ts +++ b/protocol-designer/src/file-data/selectors/utils.ts @@ -3,7 +3,6 @@ import map from 'lodash/map' import reduce from 'lodash/reduce' import { getLoadLiquidCommands } from '../../load-file/migration/utils/getLoadLiquidCommands' import { COLUMN_4_SLOTS, uuid } from '@opentrons/step-generation' - import type { AddressableAreaName, CreateCommand, @@ -21,9 +20,13 @@ import type { ModuleEntities, TimelineFrame, LiquidEntities, + PipetteEntity, + ModuleEntity, + LabwareEntity, } from '@opentrons/step-generation' +import type { Labware, Modules, Pipettes } from '../../file-types' -interface Pipettes { +interface MappedPipettes { [pipetteId: string]: { name: PipetteName } } @@ -36,7 +39,7 @@ export const getLoadCommands = ( liquidEntities: LiquidEntities, ingredLocations: LabwareLiquidState ): CreateCommand[] => { - const pipettes: Pipettes = mapValues( + const pipettes: MappedPipettes = mapValues( initialRobotState.pipettes, ( pipette: typeof initialRobotState.pipettes[keyof typeof initialRobotState.pipettes], @@ -196,3 +199,41 @@ export const getLoadCommands = ( ...loadLiquidCommands, ] } + +export const getPipettesLoadInfo = ( + pipetteEntities: PipetteEntities +): Pipettes => { + return Object.values(pipetteEntities).reduce( + (acc, pipetteEntity: PipetteEntity) => ({ + ...acc, + [pipetteEntity.id]: { pipetteName: pipetteEntity.name }, + }), + {} + ) +} + +export const getModulesLoadInfo = (moduleEntities: ModuleEntities): Modules => { + return Object.values(moduleEntities).reduce( + (acc, moduleEntity: ModuleEntity) => ({ + ...acc, + [moduleEntity.id]: { model: moduleEntity.model }, + }), + {} + ) +} + +export const getLabwareLoadInfo = ( + labwareEntities: LabwareEntities, + labwareNicknamesById: Record +): Labware => { + return Object.values(labwareEntities).reduce( + (acc, labwareEntity: LabwareEntity) => ({ + ...acc, + [labwareEntity.id]: { + displayName: labwareNicknamesById[labwareEntity.id], + labwareDefURI: labwareEntity.labwareDefURI, + }, + }), + {} + ) +} diff --git a/protocol-designer/src/file-types.ts b/protocol-designer/src/file-types.ts index 8a19aac0108..41d7a9fe076 100644 --- a/protocol-designer/src/file-types.ts +++ b/protocol-designer/src/file-types.ts @@ -1,12 +1,26 @@ +import type { + ModuleModel, + PipetteName, + ProtocolFile, +} from '@opentrons/shared-data' +import type { LiquidEntities } from '@opentrons/step-generation' import type { RootState as IngredRoot } from './labware-ingred/reducers' import type { RootState as StepformRoot } from './step-forms' import type { RootState as DismissRoot } from './dismiss' -import type { ProtocolFile as ProtocolFileV3 } from '@opentrons/shared-data/protocol/types/schemaV3' -import type { ProtocolFile as ProtocolFileV4 } from '@opentrons/shared-data/protocol/types/schemaV4' -import type { ProtocolFile as ProtocolFileV5 } from '@opentrons/shared-data/protocol/types/schemaV5' -import type { ProtocolFile as ProtocolFileV6 } from '@opentrons/shared-data/protocol/types/schemaV6' -import type { LiquidEntities } from '@opentrons/step-generation' +export interface PipetteLoadInfo { + pipetteName: PipetteName +} +export interface ModuleLoadInfo { + model: ModuleModel +} +export interface LabwareLoadInfo { + displayName: string // either labwareDef displayName or user defined nickName + labwareDefURI: string // the labware definition URI +} +export type Pipettes = Record +export type Modules = Record +export type Labware = Record export interface PDMetadata { // pipetteId to tiprackModel pipetteTiprackAssignments: Record @@ -15,19 +29,12 @@ export interface PDMetadata { ingredLocations: IngredRoot['ingredLocations'] savedStepForms: StepformRoot['savedStepForms'] orderedStepIds: StepformRoot['orderedStepIds'] - defaultValues: { - aspirate_mmFromBottom: number | null - dispense_mmFromBottom: number | null - touchTip_mmFromTop: number | null - blowout_mmFromTop: number | null - } + pipettes: Pipettes + modules: Modules + labware: Labware } -// NOTE: PD currently supports saving both v3 and v4, depending on whether it has modules -export type PDProtocolFile = - | ProtocolFileV3 - | ProtocolFileV4 - | ProtocolFileV5 - | ProtocolFileV6 + +export type PDProtocolFile = ProtocolFile export function getPDMetadata(file: PDProtocolFile): PDMetadata { const metadata = file.designerApplication?.data diff --git a/protocol-designer/src/load-file/migration/8_5_0.ts b/protocol-designer/src/load-file/migration/8_5_0.ts index 6cf583da9ae..bbf2fda4c62 100644 --- a/protocol-designer/src/load-file/migration/8_5_0.ts +++ b/protocol-designer/src/load-file/migration/8_5_0.ts @@ -2,17 +2,18 @@ import floor from 'lodash/floor' import { swatchColors } from '../../organisms/DefineLiquidsModal/swatchColors' import { getMigratedPositionFromTop } from './utils/getMigrationPositionFromTop' import { getAdditionalEquipmentLocationUpdate } from './utils/getAdditionalEquipmentLocationUpdate' +import { getEquipmentLoadInfoFromCommands } from './utils/getEquipmentLoadInfoFromCommands' import type { LoadLabwareCreateCommand, ProtocolFile, } from '@opentrons/shared-data' import type { LiquidEntities } from '@opentrons/step-generation' -import type { DesignerApplicationDataV8_5 } from '../../file-data/selectors' import type { DesignerApplicationData } from './utils/getLoadLiquidCommands' +import type { PDMetadata } from '../../file-types' export const migrateFile = ( appData: ProtocolFile -): ProtocolFile => { +): ProtocolFile => { const { designerApplication, commands, @@ -20,6 +21,7 @@ export const migrateFile = ( liquids, robot, } = appData + if (designerApplication == null || designerApplication?.data == null) { throw Error('The designerApplication key in your file is corrupt.') } @@ -156,6 +158,10 @@ export const migrateFile = ( }, {} ) + const equipmentLoadInfoFromCommands = getEquipmentLoadInfoFromCommands( + commands, + labwareDefinitions + ) return { ...appData, designerApplication: { @@ -163,6 +169,7 @@ export const migrateFile = ( data: { ...designerApplication.data, ingredients: migratedIngredients, + ...equipmentLoadInfoFromCommands, savedStepForms: { ...designerApplication.data.savedStepForms, ...updatedInitialStep, diff --git a/protocol-designer/src/load-file/migration/utils/__tests__/getEquipmentLoadInfoFromCommands.test.tsx b/protocol-designer/src/load-file/migration/utils/__tests__/getEquipmentLoadInfoFromCommands.test.tsx new file mode 100644 index 00000000000..66e265b1cbf --- /dev/null +++ b/protocol-designer/src/load-file/migration/utils/__tests__/getEquipmentLoadInfoFromCommands.test.tsx @@ -0,0 +1,67 @@ +import { describe, it, expect } from 'vitest' +import doItAllV7 from '../../../../../fixtures/protocol/7/doItAllV7.json' +import { getEquipmentLoadInfoFromCommands } from '../getEquipmentLoadInfoFromCommands' +import type { CreateCommand, LabwareDefinition2 } from '@opentrons/shared-data' +import type { EquipmentLoadInfoFromCommands } from '../getEquipmentLoadInfoFromCommands' + +describe('getEquipmentLoadInfoFromCommands', () => { + it('properly returns the pipettes, modules, and labware info for doItAllV7 fixture', () => { + const results: EquipmentLoadInfoFromCommands = { + labware: { + '239ceac8-23ec-4900-810a-70aeef880273:opentrons/nest_96_wellplate_200ul_flat/2': { + displayName: 'NEST 96 Well Plate 200 µL Flat', + labwareDefURI: 'opentrons/nest_96_wellplate_200ul_flat/2', + }, + '23ed35de-5bfd-4bb0-8f54-da99a2804ed9:opentrons/opentrons_flex_96_filtertiprack_50ul/1': { + displayName: 'Opentrons Flex 96 Filter Tip Rack 50 µL', + labwareDefURI: 'opentrons/opentrons_flex_96_filtertiprack_50ul/1', + }, + 'a793a135-06aa-4ed6-a1d3-c176c7810afa:opentrons/opentrons_24_aluminumblock_nest_1.5ml_snapcap/1': { + displayName: + 'Opentrons 24 Well Aluminum Block with NEST 1.5 mL Snapcap', + labwareDefURI: + 'opentrons/opentrons_24_aluminumblock_nest_1.5ml_snapcap/1', + }, + 'd95bb3be-b453-457c-a947-bd03dc8e56b9:opentrons/opentrons_96_flat_bottom_adapter/1': { + displayName: 'Opentrons 96 Flat Bottom Heater-Shaker Adapter', + labwareDefURI: 'opentrons/opentrons_96_flat_bottom_adapter/1', + }, + 'fcba73e7-b88e-438e-963e-f8b9a5de0983:opentrons/nest_96_wellplate_100ul_pcr_full_skirt/2': { + displayName: 'NEST 96 Well Plate 100 µL PCR Full Skirt', + labwareDefURI: 'opentrons/nest_96_wellplate_100ul_pcr_full_skirt/2', + }, + }, + modules: { + '1be16305-74e7-4bdb-9737-61ec726d2b44:magneticBlockType': { + model: 'magneticBlockV1', + }, + '627b7a27-5bb7-46de-a530-67af45652e3b:thermocyclerModuleType': { + model: 'thermocyclerModuleV2', + }, + 'c19dffa3-cb34-4702-bcf6-dcea786257d1:heaterShakerModuleType': { + model: 'heaterShakerModuleV1', + }, + 'ef44ad7f-0fd9-46d6-8bc0-c70785644cc8:temperatureModuleType': { + model: 'temperatureModuleV2', + }, + }, + pipettes: { + '2e7c6344-58ab-465c-b542-489883cb63fe': { + pipetteName: 'p1000_single_flex', + }, + '6d1e53c3-2db3-451b-ad60-3fe13781a193': { + pipetteName: 'p50_multi_flex', + }, + }, + } + + expect( + getEquipmentLoadInfoFromCommands( + doItAllV7.commands as CreateCommand[], + doItAllV7.labwareDefinitions as { + [definitionId: string]: LabwareDefinition2 + } + ) + ).toEqual(results) + }) +}) diff --git a/protocol-designer/src/load-file/migration/utils/getEquipmentLoadInfoFromCommands.ts b/protocol-designer/src/load-file/migration/utils/getEquipmentLoadInfoFromCommands.ts new file mode 100644 index 00000000000..864479c3801 --- /dev/null +++ b/protocol-designer/src/load-file/migration/utils/getEquipmentLoadInfoFromCommands.ts @@ -0,0 +1,101 @@ +import type { + CreateCommand, + LabwareDefinition2, + LoadLabwareCreateCommand, + LoadModuleCreateCommand, + LoadPipetteCreateCommand, + PipetteName, +} from '@opentrons/shared-data' +import type { Labware, Modules, Pipettes } from '../../../file-types' + +export interface EquipmentLoadInfoFromCommands { + pipettes: Pipettes + modules: Modules + labware: Labware +} +export const getEquipmentLoadInfoFromCommands = ( + commands: CreateCommand[], + labwareDefinitions: { + [definitionId: string]: LabwareDefinition2 + } +): EquipmentLoadInfoFromCommands => { + const loadPipetteCommands = commands.filter( + (command): command is LoadPipetteCreateCommand => + command.commandType === 'loadPipette' + ) + const loadLabwareCommands = commands.filter( + (command): command is LoadLabwareCreateCommand => + command.commandType === 'loadLabware' + ) + const loadModuleCommands = commands.filter( + (command): command is LoadModuleCreateCommand => + command.commandType === 'loadModule' + ) + + const pipettes = loadPipetteCommands.reduce( + (acc, loadPipette: LoadPipetteCreateCommand) => ({ + ...acc, + [loadPipette.params.pipetteId]: { + pipetteName: loadPipette.params.pipetteName as PipetteName, + }, + }), + {} + ) + + const labware = loadLabwareCommands.reduce( + (acc, loadLabware: LoadLabwareCreateCommand) => { + const { params } = loadLabware + const { + displayName: nickName, + loadName, + version, + namespace, + labwareId, + } = params + const labwareDefURI = `${namespace}/${loadName}/${version}` + + if (labwareId == null) { + console.error( + `expected to find a labwareId from loadLabware command but could not with labwareDefURI ${labwareDefURI}` + ) + } + if (labwareDefinitions[labwareDefURI] == null) { + console.error( + `expected to find matching labware definition with loadname ${loadName} but could not` + ) + } + const id = labwareId ?? 'unknown id' + const displayName = labwareDefinitions[labwareDefURI].metadata.displayName + + return { + ...acc, + [id]: { + displayName: nickName ?? displayName, + labwareDefURI, + }, + } + }, + {} + ) + + const modules = loadModuleCommands.reduce( + (acc, loadModule: LoadModuleCreateCommand) => { + const { params } = loadModule + const { moduleId, model } = params + if (moduleId == null) { + console.error( + `expected to find moduleId from loadModule command wth model ${model} but could not` + ) + } + const id = moduleId ?? 'unknown id' + return { + ...acc, + [id]: { + model, + }, + } + }, + {} + ) + return { pipettes, modules, labware } +} diff --git a/protocol-designer/src/step-forms/reducers/index.ts b/protocol-designer/src/step-forms/reducers/index.ts index b1888f28970..ef2b30ed303 100644 --- a/protocol-designer/src/step-forms/reducers/index.ts +++ b/protocol-designer/src/step-forms/reducers/index.ts @@ -45,12 +45,7 @@ import type { NormalizedAdditionalEquipmentById, NormalizedPipetteById, } from '@opentrons/step-generation' -import type { - LoadLabwareCreateCommand, - LoadModuleCreateCommand, - LoadPipetteCreateCommand, - PipetteName, -} from '@opentrons/shared-data' +import type { PipetteName } from '@opentrons/shared-data' import type { RootState as LabwareDefsRootState } from '../../labware-defs' import type { LoadFileAction } from '../../load-file' import type { SaveStepFormAction } from '../../ui/steps/actions/thunks' @@ -100,6 +95,7 @@ import type { SelectMultipleStepsAction, } from '../../ui/steps/actions/types' import type { Action } from '../../types' +import type { ModuleLoadInfo, PipetteLoadInfo } from '../../file-types' import type { AdditionalEquipmentLocationUpdate, LocationUpdate, @@ -995,41 +991,8 @@ export const labwareInvariantProperties: Reducer< action: LoadFileAction ): NormalizedLabwareById => { const { file } = action.payload - const loadLabwareCommands = Object.values(file.commands).filter( - (command): command is LoadLabwareCreateCommand => - command.commandType === 'loadLabware' - ) - const labware = { - ...loadLabwareCommands.reduce( - (acc: NormalizedLabwareById, command: LoadLabwareCreateCommand) => { - const { labwareId, loadName, namespace, version } = command.params - const labwareDefinitionMatch = Object.entries( - file.labwareDefinitions - ).find( - ([definitionUri, labwareDef]) => - labwareDef.parameters.loadName === loadName && - labwareDef.namespace === namespace && - labwareDef.version === version - ) - if (labwareDefinitionMatch == null) { - console.error( - `expected to find labware definition match with loadname ${loadName} but could not` - ) - } - const labwareDefURI = - labwareDefinitionMatch != null ? labwareDefinitionMatch[0] : '' - const id = labwareId ?? '' - return { - ...acc, - [id]: { - labwareDefURI, - }, - } - }, - {} - ), - } - return { ...labware, ...state } + const metadata = getPDMetadata(file) + return { ...metadata.labware, ...state } }, REPLACE_CUSTOM_LABWARE_DEF: ( state: NormalizedLabwareById, @@ -1085,30 +1048,22 @@ export const moduleInvariantProperties: Reducer< action: LoadFileAction ): ModuleEntities => { const { file } = action.payload - const loadModuleCommands = Object.values(file.commands).filter( - (command): command is LoadModuleCreateCommand => - command.commandType === 'loadModule' - ) - const modules = loadModuleCommands.reduce( - (acc: ModuleEntities, command: LoadModuleCreateCommand) => { - const { moduleId, model, location } = command.params - if (moduleId == null) { - console.error( - `expected module ${model} in location ${location.slotName} to have an id, but id does not` - ) - return acc - } - return { - ...acc, - [moduleId]: { - id: moduleId, - type: getModuleType(model), - model, - }, + const metadata = getPDMetadata(file) + const modules: ModuleEntities = Object.entries(metadata.modules).reduce( + ( + acc: ModuleEntities, + [id, moduleLoadInfo]: [string, ModuleLoadInfo] + ) => { + acc[id] = { + id: id, + type: getModuleType(moduleLoadInfo.model), + model: moduleLoadInfo.model, } + return acc }, {} ) + return Object.keys(modules).length > 0 ? modules : state }, }, @@ -1119,7 +1074,6 @@ export const pipetteInvariantProperties: Reducer< NormalizedPipetteById, any // @ts-expect-error(sa, 2021-6-10): cannot use string literals as action type - // TODO IMMEDIATELY: refactor this to the old fashioned way if we cannot have type safety: https://github.com/redux-utilities/redux-actions/issues/282#issuecomment-595163081 > = handleActions( { LOAD_FILE: ( @@ -1128,20 +1082,18 @@ export const pipetteInvariantProperties: Reducer< ): NormalizedPipetteById => { const { file } = action.payload const metadata = getPDMetadata(file) - const loadPipetteCommands = Object.values(file.commands).filter( - (command): command is LoadPipetteCreateCommand => - command.commandType === 'loadPipette' - ) - const pipettes = loadPipetteCommands.reduce( - (acc: NormalizedPipetteById, command) => { - const { pipetteName, pipetteId } = command.params - const tiprackDefURI = metadata.pipetteTiprackAssignments[pipetteId] + const pipettes = Object.entries(metadata.pipettes).reduce( + ( + acc: NormalizedPipetteById, + [id, pipetteLoadInfo]: [string, PipetteLoadInfo] + ) => { + const tiprackDefURI = metadata.pipetteTiprackAssignments[id] return { ...acc, - [pipetteId]: { - id: pipetteId, - name: pipetteName as PipetteName, + [id]: { + id, + name: pipetteLoadInfo.pipetteName as PipetteName, tiprackDefURI, }, }