From 3b3f39c6230da2caeacd89d9dd8ff56e98b5af5b Mon Sep 17 00:00:00 2001 From: Jethary Date: Mon, 10 Feb 2025 14:38:07 -0500 Subject: [PATCH 1/8] feat(protocol-designer): load module in python file --- .../src/file-data/selectors/fileCreator.ts | 24 ++++++++------- .../src/file-data/selectors/pythonFile.ts | 30 +++++++++++++++++-- 2 files changed, 41 insertions(+), 13 deletions(-) diff --git a/protocol-designer/src/file-data/selectors/fileCreator.ts b/protocol-designer/src/file-data/selectors/fileCreator.ts index a12547a3b70..2a0d99c8cc3 100644 --- a/protocol-designer/src/file-data/selectors/fileCreator.ts +++ b/protocol-designer/src/file-data/selectors/fileCreator.ts @@ -99,34 +99,34 @@ export const createFile: Selector = createSelector( getRobotStateTimeline, getRobotType, dismissSelectors.getAllDismissedWarnings, - stepFormSelectors.getLiquidEntities, ingredSelectors.getLiquidsByLabwareId, stepFormSelectors.getSavedStepForms, stepFormSelectors.getOrderedStepIds, - stepFormSelectors.getLabwareEntities, - stepFormSelectors.getModuleEntities, - stepFormSelectors.getPipetteEntities, uiLabwareSelectors.getLabwareNicknamesById, labwareDefSelectors.getLabwareDefsByURI, getStepGroups, + stepFormSelectors.getInvariantContext, ( fileMetadata, initialRobotState, robotStateTimeline, robotType, dismissedWarnings, - liquidEntities, ingredLocations, savedStepForms, orderedStepIds, - labwareEntities, - moduleEntities, - pipetteEntities, labwareNicknamesById, labwareDefsByURI, - stepGroups + stepGroups, + invariantContext ) => { const { author, description, created } = fileMetadata + const { + pipetteEntities, + labwareEntities, + liquidEntities, + moduleEntities, + } = invariantContext const loadCommands = getLoadCommands( initialRobotState, @@ -308,14 +308,16 @@ export const createFile: Selector = createSelector( export const createPythonFile: Selector = createSelector( getFileMetadata, getRobotType, - (fileMetadata, robotType) => { + stepFormSelectors.getInvariantContext, + getInitialRobotState, + (fileMetadata, robotType, invariantContext, robotState) => { return ( [ // Here are the sections of the Python file: pythonImports(), pythonMetadata(fileMetadata), pythonRequirements(robotType), - pythonDefRun(), + pythonDefRun(invariantContext, robotState), ] .filter(section => section) // skip any blank sections .join('\n\n') + '\n' diff --git a/protocol-designer/src/file-data/selectors/pythonFile.ts b/protocol-designer/src/file-data/selectors/pythonFile.ts index e4d9e63eec0..549951b6b55 100644 --- a/protocol-designer/src/file-data/selectors/pythonFile.ts +++ b/protocol-designer/src/file-data/selectors/pythonFile.ts @@ -4,7 +4,10 @@ import { FLEX_ROBOT_TYPE, OT2_ROBOT_TYPE } from '@opentrons/shared-data' import { formatPyDict, indentPyLines, + InvariantContext, + ModuleEntities, PROTOCOL_CONTEXT_NAME, + TimelineFrame, } from '@opentrons/step-generation' import type { FileMetadataFields } from '../types' import type { RobotType } from '@opentrons/shared-data' @@ -51,9 +54,32 @@ export function pythonRequirements(robotType: RobotType): string { return `requirements = ${formatPyDict(requirements)}` } -export function pythonDefRun(): string { +function getLoadModules( + moduleEntities: ModuleEntities, + moduleRobotState: TimelineFrame['modules'] +): string[] { + const pythonModules = Object.values(moduleEntities).reduce( + (acc, moduleEntity) => [ + ...acc, + `${moduleEntity.pythonName} = ${PROTOCOL_CONTEXT_NAME}.load_module("${ + moduleEntity.model + }", "${moduleRobotState[moduleEntity.id].slot}")`, + ], + [] + ) + return pythonModules +} + +export function pythonDefRun( + invariantContext: InvariantContext, + robotState: TimelineFrame +): string { + const loadModules = getLoadModules( + invariantContext.moduleEntities, + robotState.modules + ) const sections: string[] = [ - // loadModules(), + ...loadModules, // loadLabware(), // loadInstruments(), // defineLiquids(), From 0184ec0c2cd004cf0486dee7aa42a1ea15eadc61 Mon Sep 17 00:00:00 2001 From: Jethary Date: Mon, 10 Feb 2025 14:40:14 -0500 Subject: [PATCH 2/8] remove changes to reselect --- protocol-designer/typings/reselect.d.ts | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) diff --git a/protocol-designer/typings/reselect.d.ts b/protocol-designer/typings/reselect.d.ts index de43b405f6a..6b7937799c4 100644 --- a/protocol-designer/typings/reselect.d.ts +++ b/protocol-designer/typings/reselect.d.ts @@ -1,6 +1,5 @@ import type { OutputSelector, Selector } from 'reselect' declare module 'reselect' { - // declaring type for createSelector with 15 selectors because the reselect types only support up to 12 selectors export function createSelector< S, R1, @@ -15,9 +14,6 @@ declare module 'reselect' { R10, R11, R12, - R13, - R14, - R15, T >( selector1: Selector, @@ -32,9 +28,6 @@ declare module 'reselect' { selector10: Selector, selector11: Selector, selector12: Selector, - selector13: Selector, - selector14: Selector, - selector15: Selector, combiner: ( res1: R1, res2: R2, @@ -47,10 +40,7 @@ declare module 'reselect' { res9: R9, res10: R10, res11: R11, - res12: R12, - res13: R13, - res14: R14, - res15: R15 + res12: R12 ) => T ): OutputSelector< S, @@ -67,10 +57,7 @@ declare module 'reselect' { res9: R9, res10: R10, res11: R11, - res12: R12, - res13: R13, - res14: R14, - res15: R15 + res12: R12 ) => T > } From 3bb3a87a55020791fd1aa9017e45cc39976df8b3 Mon Sep 17 00:00:00 2001 From: Jethary Date: Mon, 10 Feb 2025 14:51:30 -0500 Subject: [PATCH 3/8] fix import error --- protocol-designer/src/file-data/selectors/pythonFile.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/protocol-designer/src/file-data/selectors/pythonFile.ts b/protocol-designer/src/file-data/selectors/pythonFile.ts index 549951b6b55..eb6daa6d116 100644 --- a/protocol-designer/src/file-data/selectors/pythonFile.ts +++ b/protocol-designer/src/file-data/selectors/pythonFile.ts @@ -4,13 +4,15 @@ import { FLEX_ROBOT_TYPE, OT2_ROBOT_TYPE } from '@opentrons/shared-data' import { formatPyDict, indentPyLines, + PROTOCOL_CONTEXT_NAME, +} from '@opentrons/step-generation' +import type { InvariantContext, ModuleEntities, - PROTOCOL_CONTEXT_NAME, TimelineFrame, } from '@opentrons/step-generation' -import type { FileMetadataFields } from '../types' import type { RobotType } from '@opentrons/shared-data' +import type { FileMetadataFields } from '../types' const PAPI_VERSION = '2.23' // latest version from api/src/opentrons/protocols/api_support/definitions.py From a08634a1e832f6aae83d72a365006d3649f2c979 Mon Sep 17 00:00:00 2001 From: Jethary Date: Mon, 10 Feb 2025 15:10:24 -0500 Subject: [PATCH 4/8] fix unit test --- .../file-data/__tests__/createFile.test.ts | 20 +++++++++++++------ .../src/file-data/selectors/pythonFile.ts | 8 ++++---- 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/protocol-designer/src/file-data/__tests__/createFile.test.ts b/protocol-designer/src/file-data/__tests__/createFile.test.ts index 527e1cccf20..4b3ad1103ca 100644 --- a/protocol-designer/src/file-data/__tests__/createFile.test.ts +++ b/protocol-designer/src/file-data/__tests__/createFile.test.ts @@ -70,6 +70,12 @@ describe('createFile selector', () => { afterEach(() => { vi.restoreAllMocks() }) + const entities = { + moduleEntities: v7Fixture.moduleEntities, + labwareEntities, + pipetteEntities, + liquidEntities: ingredients, + } it('should return a schema-valid JSON V8 protocol', () => { // @ts-expect-error(sa, 2021-6-15): resultFunc not part of Selector type const result = createFile.resultFunc( @@ -78,16 +84,13 @@ describe('createFile selector', () => { v7Fixture.robotStateTimeline, OT2_ROBOT_TYPE, dismissedWarnings, - ingredients, ingredLocations, v7Fixture.savedStepForms, v7Fixture.orderedStepIds, - labwareEntities, - v7Fixture.moduleEntities, - pipetteEntities, labwareNicknamesById, labwareDefsByURI, - {} + {}, + entities ) expectResultToMatchSchema(result) @@ -99,7 +102,12 @@ describe('createFile selector', () => { it('should return a valid Python protocol file', () => { // @ts-expect-error(sa, 2021-6-15): resultFunc not part of Selector type - const result = createPythonFile.resultFunc(fileMetadata, OT2_ROBOT_TYPE, {}) + const result = createPythonFile.resultFunc( + fileMetadata, + OT2_ROBOT_TYPE, + entities, + v7Fixture.initialRobotState + ) // This is just a quick smoke test to make sure createPythonFile() produces // something that looks like a Python file. The individual sections of the // generated Python will be tested in separate unit tests. diff --git a/protocol-designer/src/file-data/selectors/pythonFile.ts b/protocol-designer/src/file-data/selectors/pythonFile.ts index eb6daa6d116..1fc6c3cfe47 100644 --- a/protocol-designer/src/file-data/selectors/pythonFile.ts +++ b/protocol-designer/src/file-data/selectors/pythonFile.ts @@ -76,10 +76,10 @@ export function pythonDefRun( invariantContext: InvariantContext, robotState: TimelineFrame ): string { - const loadModules = getLoadModules( - invariantContext.moduleEntities, - robotState.modules - ) + const { moduleEntities } = invariantContext + + const loadModules = getLoadModules(moduleEntities, robotState.modules) + const sections: string[] = [ ...loadModules, // loadLabware(), From 40824952fa4ed09fbad9127732572b640546729e Mon Sep 17 00:00:00 2001 From: Jethary Date: Mon, 10 Feb 2025 16:50:32 -0500 Subject: [PATCH 5/8] address feedback, add to test --- .../file-data/__tests__/pythonFile.test.ts | 49 ++++++++++++++++++- .../src/file-data/selectors/pythonFile.ts | 28 ++++++----- .../src/file-data/selectors/utils.ts | 23 +++++++++ protocol-designer/typings/reselect.d.ts | 17 ++++++- 4 files changed, 100 insertions(+), 17 deletions(-) diff --git a/protocol-designer/src/file-data/__tests__/pythonFile.test.ts b/protocol-designer/src/file-data/__tests__/pythonFile.test.ts index cb7f526606f..2c234a63bbe 100644 --- a/protocol-designer/src/file-data/__tests__/pythonFile.test.ts +++ b/protocol-designer/src/file-data/__tests__/pythonFile.test.ts @@ -1,6 +1,16 @@ import { describe, it, expect } from 'vitest' -import { FLEX_ROBOT_TYPE, OT2_ROBOT_TYPE } from '@opentrons/shared-data' -import { pythonMetadata, pythonRequirements } from '../selectors/pythonFile' +import { + FLEX_ROBOT_TYPE, + MAGNETIC_BLOCK_TYPE, + MAGNETIC_BLOCK_V1, + OT2_ROBOT_TYPE, +} from '@opentrons/shared-data' +import { + pythonDefRun, + pythonMetadata, + pythonRequirements, +} from '../selectors/pythonFile' +import { InvariantContext, TimelineFrame } from '@opentrons/step-generation' describe('pythonMetadata', () => { it('should generate metadata section', () => { @@ -50,3 +60,38 @@ requirements = { ) }) }) + +const moduleId = '1' +const mockInvariantContext: InvariantContext = { + additionalEquipmentEntities: {}, + pipetteEntities: {}, + labwareEntities: {}, + liquidEntities: {}, + moduleEntities: { + [moduleId]: { + id: moduleId, + model: MAGNETIC_BLOCK_V1, + type: MAGNETIC_BLOCK_TYPE, + pythonName: 'magnetic_block_1', + }, + }, + config: { OT_PD_DISABLE_MODULE_RESTRICTIONS: true }, +} +const mockInitialRobotState: TimelineFrame = { + pipettes: {}, + labware: {}, + modules: { [moduleId]: { slot: 'B1', moduleState: {} as any } }, + tipState: { tipracks: {}, pipettes: {} }, + liquidState: { pipettes: {}, labware: {}, additionalEquipment: {} }, +} +describe('pythonDefRun', () => { + it('should generate the commands section', () => { + expect(pythonDefRun(mockInvariantContext, mockInitialRobotState)).toBe( + ` +def run(protocol: protocol_api.ProtocolContext): + + # Load Modules: + magnetic_block_1 = protocol.load_module("magneticBlockV1", "B1")`.trimStart() + ) + }) +}) diff --git a/protocol-designer/src/file-data/selectors/pythonFile.ts b/protocol-designer/src/file-data/selectors/pythonFile.ts index 1fc6c3cfe47..74e69ebb142 100644 --- a/protocol-designer/src/file-data/selectors/pythonFile.ts +++ b/protocol-designer/src/file-data/selectors/pythonFile.ts @@ -6,6 +6,7 @@ import { indentPyLines, PROTOCOL_CONTEXT_NAME, } from '@opentrons/step-generation' +import { getModulePythonIdentifier } from './utils' import type { InvariantContext, ModuleEntities, @@ -59,17 +60,18 @@ export function pythonRequirements(robotType: RobotType): string { function getLoadModules( moduleEntities: ModuleEntities, moduleRobotState: TimelineFrame['modules'] -): string[] { - const pythonModules = Object.values(moduleEntities).reduce( - (acc, moduleEntity) => [ - ...acc, - `${moduleEntity.pythonName} = ${PROTOCOL_CONTEXT_NAME}.load_module("${ - moduleEntity.model - }", "${moduleRobotState[moduleEntity.id].slot}")`, - ], - [] - ) - return pythonModules +): string { + const pythonModules = Object.values(moduleEntities) + .map(module => { + const pythonIdentifier = getModulePythonIdentifier(module.model) + return `${ + module.pythonName + } = ${PROTOCOL_CONTEXT_NAME}.load_module("${pythonIdentifier}", "${ + moduleRobotState[module.id].slot + }")` + }) + .join('\n') + return `# Load Modules:\n${pythonModules}` } export function pythonDefRun( @@ -81,7 +83,7 @@ export function pythonDefRun( const loadModules = getLoadModules(moduleEntities, robotState.modules) const sections: string[] = [ - ...loadModules, + loadModules, // loadLabware(), // loadInstruments(), // defineLiquids(), @@ -93,7 +95,7 @@ export function pythonDefRun( .filter(section => section) // skip empty sections .join('\n\n') || 'pass' return ( - `def run(${PROTOCOL_CONTEXT_NAME}: protocol_api.ProtocolContext):\n` + + `def run(${PROTOCOL_CONTEXT_NAME}: protocol_api.ProtocolContext):\n\n` + `${indentPyLines(functionBody)}` ) } diff --git a/protocol-designer/src/file-data/selectors/utils.ts b/protocol-designer/src/file-data/selectors/utils.ts index c8f6e3ff1f2..5bcc18b2777 100644 --- a/protocol-designer/src/file-data/selectors/utils.ts +++ b/protocol-designer/src/file-data/selectors/utils.ts @@ -10,6 +10,7 @@ import type { LoadLabwareCreateCommand, LoadModuleCreateCommand, LoadPipetteCreateCommand, + ModuleModel, PipetteName, } from '@opentrons/shared-data' import type { @@ -237,3 +238,25 @@ export const getLabwareLoadInfo = ( {} ) } + +export const getModulePythonIdentifier = (model: ModuleModel): string => { + switch (model) { + case 'thermocyclerModuleV1': { + return 'thermocycler' + } + case 'magneticModuleV2': { + return 'magnetic module gen2' + } + case 'temperatureModuleV2': { + return 'temperature moduel gen2' + } + case 'magneticModuleV1': { + return 'magdeck' + } + case 'temperatureModuleV1': { + return 'tempdeck' + } + default: + return model + } +} diff --git a/protocol-designer/typings/reselect.d.ts b/protocol-designer/typings/reselect.d.ts index 6b7937799c4..de43b405f6a 100644 --- a/protocol-designer/typings/reselect.d.ts +++ b/protocol-designer/typings/reselect.d.ts @@ -1,5 +1,6 @@ import type { OutputSelector, Selector } from 'reselect' declare module 'reselect' { + // declaring type for createSelector with 15 selectors because the reselect types only support up to 12 selectors export function createSelector< S, R1, @@ -14,6 +15,9 @@ declare module 'reselect' { R10, R11, R12, + R13, + R14, + R15, T >( selector1: Selector, @@ -28,6 +32,9 @@ declare module 'reselect' { selector10: Selector, selector11: Selector, selector12: Selector, + selector13: Selector, + selector14: Selector, + selector15: Selector, combiner: ( res1: R1, res2: R2, @@ -40,7 +47,10 @@ declare module 'reselect' { res9: R9, res10: R10, res11: R11, - res12: R12 + res12: R12, + res13: R13, + res14: R14, + res15: R15 ) => T ): OutputSelector< S, @@ -57,7 +67,10 @@ declare module 'reselect' { res9: R9, res10: R10, res11: R11, - res12: R12 + res12: R12, + res13: R13, + res14: R14, + res15: R15 ) => T > } From a7ba1570f374d6df0502809de8fc0d92342a15db Mon Sep 17 00:00:00 2001 From: Jethary Date: Mon, 10 Feb 2025 17:01:57 -0500 Subject: [PATCH 6/8] account for no modules --- .../file-data/__tests__/pythonFile.test.ts | 1 - .../src/file-data/selectors/pythonFile.ts | 27 ++++++++++--------- 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/protocol-designer/src/file-data/__tests__/pythonFile.test.ts b/protocol-designer/src/file-data/__tests__/pythonFile.test.ts index 2c234a63bbe..dddd4ae0b38 100644 --- a/protocol-designer/src/file-data/__tests__/pythonFile.test.ts +++ b/protocol-designer/src/file-data/__tests__/pythonFile.test.ts @@ -89,7 +89,6 @@ describe('pythonDefRun', () => { expect(pythonDefRun(mockInvariantContext, mockInitialRobotState)).toBe( ` def run(protocol: protocol_api.ProtocolContext): - # Load Modules: magnetic_block_1 = protocol.load_module("magneticBlockV1", "B1")`.trimStart() ) diff --git a/protocol-designer/src/file-data/selectors/pythonFile.ts b/protocol-designer/src/file-data/selectors/pythonFile.ts index 74e69ebb142..2089c0a8540 100644 --- a/protocol-designer/src/file-data/selectors/pythonFile.ts +++ b/protocol-designer/src/file-data/selectors/pythonFile.ts @@ -61,17 +61,20 @@ function getLoadModules( moduleEntities: ModuleEntities, moduleRobotState: TimelineFrame['modules'] ): string { - const pythonModules = Object.values(moduleEntities) - .map(module => { - const pythonIdentifier = getModulePythonIdentifier(module.model) - return `${ - module.pythonName - } = ${PROTOCOL_CONTEXT_NAME}.load_module("${pythonIdentifier}", "${ - moduleRobotState[module.id].slot - }")` - }) - .join('\n') - return `# Load Modules:\n${pythonModules}` + const hasModules = Object.keys(moduleEntities).length > 0 + const pythonModules = hasModules + ? Object.values(moduleEntities) + .map(module => { + const pythonIdentifier = getModulePythonIdentifier(module.model) + return `${ + module.pythonName + } = ${PROTOCOL_CONTEXT_NAME}.load_module("${pythonIdentifier}", "${ + moduleRobotState[module.id].slot + }")` + }) + .join('\n') + : '' + return hasModules ? `# Load Modules:\n${pythonModules}` : '' } export function pythonDefRun( @@ -95,7 +98,7 @@ export function pythonDefRun( .filter(section => section) // skip empty sections .join('\n\n') || 'pass' return ( - `def run(${PROTOCOL_CONTEXT_NAME}: protocol_api.ProtocolContext):\n\n` + + `def run(${PROTOCOL_CONTEXT_NAME}: protocol_api.ProtocolContext):\n` + `${indentPyLines(functionBody)}` ) } From e84bed5f3e3989009f6a97307976ce3f5cced559 Mon Sep 17 00:00:00 2001 From: Jethary Date: Tue, 11 Feb 2025 08:30:43 -0500 Subject: [PATCH 7/8] refine loadModules fn and test --- .../file-data/__tests__/pythonFile.test.ts | 73 +++++++++++-------- .../src/file-data/selectors/pythonFile.ts | 13 ++-- .../src/file-data/selectors/utils.ts | 23 ------ 3 files changed, 48 insertions(+), 61 deletions(-) diff --git a/protocol-designer/src/file-data/__tests__/pythonFile.test.ts b/protocol-designer/src/file-data/__tests__/pythonFile.test.ts index dddd4ae0b38..27fb606865d 100644 --- a/protocol-designer/src/file-data/__tests__/pythonFile.test.ts +++ b/protocol-designer/src/file-data/__tests__/pythonFile.test.ts @@ -1,16 +1,19 @@ import { describe, it, expect } from 'vitest' import { FLEX_ROBOT_TYPE, + HEATERSHAKER_MODULE_TYPE, + HEATERSHAKER_MODULE_V1, MAGNETIC_BLOCK_TYPE, MAGNETIC_BLOCK_V1, OT2_ROBOT_TYPE, } from '@opentrons/shared-data' import { - pythonDefRun, + getLoadModules, pythonMetadata, pythonRequirements, } from '../selectors/pythonFile' -import { InvariantContext, TimelineFrame } from '@opentrons/step-generation' +import type { TimelineFrame } from '@opentrons/step-generation' +import type { ModuleEntities } from '../../step-forms' describe('pythonMetadata', () => { it('should generate metadata section', () => { @@ -61,36 +64,42 @@ requirements = { }) }) -const moduleId = '1' -const mockInvariantContext: InvariantContext = { - additionalEquipmentEntities: {}, - pipetteEntities: {}, - labwareEntities: {}, - liquidEntities: {}, - moduleEntities: { - [moduleId]: { - id: moduleId, - model: MAGNETIC_BLOCK_V1, - type: MAGNETIC_BLOCK_TYPE, - pythonName: 'magnetic_block_1', - }, - }, - config: { OT_PD_DISABLE_MODULE_RESTRICTIONS: true }, -} -const mockInitialRobotState: TimelineFrame = { - pipettes: {}, - labware: {}, - modules: { [moduleId]: { slot: 'B1', moduleState: {} as any } }, - tipState: { tipracks: {}, pipettes: {} }, - liquidState: { pipettes: {}, labware: {}, additionalEquipment: {} }, -} -describe('pythonDefRun', () => { - it('should generate the commands section', () => { - expect(pythonDefRun(mockInvariantContext, mockInitialRobotState)).toBe( - ` -def run(protocol: protocol_api.ProtocolContext): - # Load Modules: - magnetic_block_1 = protocol.load_module("magneticBlockV1", "B1")`.trimStart() +describe('getLoadModules', () => { + it('should generate loadModules', () => { + const moduleId = '1' + const moduleId2 = '2' + const moduleId3 = '3' + const mockModuleEntities: ModuleEntities = { + [moduleId]: { + id: moduleId, + model: MAGNETIC_BLOCK_V1, + type: MAGNETIC_BLOCK_TYPE, + pythonName: 'magnetic_block_1', + }, + [moduleId2]: { + id: moduleId2, + model: HEATERSHAKER_MODULE_V1, + type: HEATERSHAKER_MODULE_TYPE, + pythonName: 'heater_shaker_1', + }, + [moduleId3]: { + id: moduleId3, + model: MAGNETIC_BLOCK_V1, + type: MAGNETIC_BLOCK_TYPE, + pythonName: 'magnetic_block_2', + }, + } + const modules: TimelineFrame['modules'] = { + [moduleId]: { slot: 'B1', moduleState: {} as any }, + [moduleId2]: { slot: 'A1', moduleState: {} as any }, + [moduleId3]: { slot: 'A2', moduleState: {} as any }, + } + + expect(getLoadModules(mockModuleEntities, modules)).toBe( + `# Load Modules: +magnetic_block_1 = protocol.load_module("magneticBlockV1", "B1") +heater_shaker_1 = protocol.load_module("heaterShakerModuleV1", "A1") +magnetic_block_2 = protocol.load_module("magneticBlockV1", "A2")` ) }) }) diff --git a/protocol-designer/src/file-data/selectors/pythonFile.ts b/protocol-designer/src/file-data/selectors/pythonFile.ts index 2089c0a8540..a20d69ee25f 100644 --- a/protocol-designer/src/file-data/selectors/pythonFile.ts +++ b/protocol-designer/src/file-data/selectors/pythonFile.ts @@ -3,10 +3,10 @@ import { FLEX_ROBOT_TYPE, OT2_ROBOT_TYPE } from '@opentrons/shared-data' import { formatPyDict, + formatPyStr, indentPyLines, PROTOCOL_CONTEXT_NAME, } from '@opentrons/step-generation' -import { getModulePythonIdentifier } from './utils' import type { InvariantContext, ModuleEntities, @@ -57,7 +57,7 @@ export function pythonRequirements(robotType: RobotType): string { return `requirements = ${formatPyDict(requirements)}` } -function getLoadModules( +export function getLoadModules( moduleEntities: ModuleEntities, moduleRobotState: TimelineFrame['modules'] ): string { @@ -65,12 +65,13 @@ function getLoadModules( const pythonModules = hasModules ? Object.values(moduleEntities) .map(module => { - const pythonIdentifier = getModulePythonIdentifier(module.model) + // pythonIdentifier from api/src/opentrons/protocol_api/validation.py#L373 + const pythonIdentifier = module.model return `${ module.pythonName - } = ${PROTOCOL_CONTEXT_NAME}.load_module("${pythonIdentifier}", "${ - moduleRobotState[module.id].slot - }")` + } = ${PROTOCOL_CONTEXT_NAME}.load_module(${formatPyStr( + pythonIdentifier + )}, ${formatPyStr(moduleRobotState[module.id].slot)})` }) .join('\n') : '' diff --git a/protocol-designer/src/file-data/selectors/utils.ts b/protocol-designer/src/file-data/selectors/utils.ts index 5bcc18b2777..c8f6e3ff1f2 100644 --- a/protocol-designer/src/file-data/selectors/utils.ts +++ b/protocol-designer/src/file-data/selectors/utils.ts @@ -10,7 +10,6 @@ import type { LoadLabwareCreateCommand, LoadModuleCreateCommand, LoadPipetteCreateCommand, - ModuleModel, PipetteName, } from '@opentrons/shared-data' import type { @@ -238,25 +237,3 @@ export const getLabwareLoadInfo = ( {} ) } - -export const getModulePythonIdentifier = (model: ModuleModel): string => { - switch (model) { - case 'thermocyclerModuleV1': { - return 'thermocycler' - } - case 'magneticModuleV2': { - return 'magnetic module gen2' - } - case 'temperatureModuleV2': { - return 'temperature moduel gen2' - } - case 'magneticModuleV1': { - return 'magdeck' - } - case 'temperatureModuleV1': { - return 'tempdeck' - } - default: - return model - } -} From 9e063ce874ee0c3c811b5b7aa02984c8d8605cab Mon Sep 17 00:00:00 2001 From: Jethary Date: Tue, 11 Feb 2025 10:50:02 -0500 Subject: [PATCH 8/8] call module.model inline --- protocol-designer/src/file-data/selectors/pythonFile.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/protocol-designer/src/file-data/selectors/pythonFile.ts b/protocol-designer/src/file-data/selectors/pythonFile.ts index a20d69ee25f..59d8b747d6f 100644 --- a/protocol-designer/src/file-data/selectors/pythonFile.ts +++ b/protocol-designer/src/file-data/selectors/pythonFile.ts @@ -65,12 +65,11 @@ export function getLoadModules( const pythonModules = hasModules ? Object.values(moduleEntities) .map(module => { - // pythonIdentifier from api/src/opentrons/protocol_api/validation.py#L373 - const pythonIdentifier = module.model + // pythonIdentifier (module.model) from api/src/opentrons/protocol_api/validation.py#L373 return `${ module.pythonName } = ${PROTOCOL_CONTEXT_NAME}.load_module(${formatPyStr( - pythonIdentifier + module.model )}, ${formatPyStr(moduleRobotState[module.id].slot)})` }) .join('\n')