From 710cffa80d6f36f653bc51c9934ee0916a6bc521 Mon Sep 17 00:00:00 2001 From: Shlok Amin Date: Fri, 7 Feb 2020 10:07:45 -0500 Subject: [PATCH] feat(protocol-designer): hook up wait for temp in PD (#4926) * feat(protocol-designer): hook up wait for temp in PD Adds new command creator for await temp, adds warnings as specified in ticket fix #4732 * refactor await temperature test * add comment explaining unreachable temp * clean up imports * refactor step generation tests to use same robot factory function * plug in forAwaitTemperature state updater * adds in missing temp step error text into localization * removed comments * Use temperature instead of temp to avoid confusion * move mock object creator into existing fixture file * rename test name to be more understandable * refactor test to reuse mock factory function * import status constants into test fixture file * update test names to reflect action and outcome * remove leftover console.log --- .../src/file-data/selectors/commands.js | 5 + .../src/localization/en/alert.json | 4 + .../atomic/awaitTemperature.js | 62 +++++++ .../commandCreators/atomic/index.js | 2 + .../step-generation/commandCreators/index.js | 1 + .../src/step-generation/errorCreators.js | 8 + .../getNextRobotStateAndWarnings/index.js | 7 + .../temperatureUpdates.js | 24 ++- .../src/step-generation/index.js | 1 + .../test-with-flow/awaitTemperature.test.js | 150 ++++++++++++++++ .../fixtures/robotStateFixtures.js | 21 +++ .../test-with-flow/temperatureUpdates.test.js | 163 +++++++++++++----- .../src/step-generation/types.js | 9 + .../src/step-generation/utils/misc.js | 2 +- .../stepFormToArgs/pauseFormToArgs.js | 66 ++++--- .../test/pauseFormToArgs.test.js | 72 ++++++++ 16 files changed, 528 insertions(+), 69 deletions(-) create mode 100644 protocol-designer/src/step-generation/commandCreators/atomic/awaitTemperature.js create mode 100644 protocol-designer/src/step-generation/test-with-flow/awaitTemperature.test.js create mode 100644 protocol-designer/src/steplist/formLevel/stepFormToArgs/test/pauseFormToArgs.test.js diff --git a/protocol-designer/src/file-data/selectors/commands.js b/protocol-designer/src/file-data/selectors/commands.js index f3597bf0bf5..d80bd651281 100644 --- a/protocol-designer/src/file-data/selectors/commands.js +++ b/protocol-designer/src/file-data/selectors/commands.js @@ -147,6 +147,11 @@ const commandCreatorFromStepArgs = ( StepGeneration.deactivateTemperature, args ) + case 'awaitTemperature': + return StepGeneration.curryCommandCreator( + StepGeneration.awaitTemperature, + args + ) } console.warn(`unhandled commandCreatorFnName: ${args.commandCreatorFnName}`) return null diff --git a/protocol-designer/src/localization/en/alert.json b/protocol-designer/src/localization/en/alert.json index ad772768624..f292f35e6ff 100644 --- a/protocol-designer/src/localization/en/alert.json +++ b/protocol-designer/src/localization/en/alert.json @@ -46,6 +46,10 @@ "MISSING_MODULE": { "title": "Missing module for step", "body": "A step requires a module that does not exist" + }, + "MISSING_TEMPERATURE_STEP": { + "title": "Missing Temperature step", + "body": "Add a Temperature step prior to this Pause step. The module is not currently changing temperature because it has either been deactivated or is holding a temperature" } }, "warning": { diff --git a/protocol-designer/src/step-generation/commandCreators/atomic/awaitTemperature.js b/protocol-designer/src/step-generation/commandCreators/atomic/awaitTemperature.js new file mode 100644 index 00000000000..f167eeea64a --- /dev/null +++ b/protocol-designer/src/step-generation/commandCreators/atomic/awaitTemperature.js @@ -0,0 +1,62 @@ +// @flow +import { + TEMPDECK, + TEMPERATURE_AT_TARGET, + TEMPERATURE_DEACTIVATED, +} from '../../../constants' +import * as errorCreators from '../../errorCreators' +import type { CommandCreator, AwaitTemperatureArgs } from '../../types' +import { getModuleState } from '../../utils/misc' + +/** Set temperature target for specified module. */ +export const awaitTemperature: CommandCreator = ( + args, + invariantContext, + prevRobotState +) => { + const { module, temperature } = args + const tempModState = module ? getModuleState(prevRobotState, module) : null + + if (module === null || !tempModState) { + return { errors: [errorCreators.missingModuleError()] } + } + + if (tempModState.type !== TEMPDECK) { + console.error( + `expected module to be ${TEMPDECK} but got ${tempModState.type}` + ) + return { errors: [errorCreators.missingModuleError()] } + } + + // if the temp mod is already at the target temp + // AND the newly awaited temperature is different than the target temp + // this means the temp mod will not change its temp, since it is already + // at the target temp, so the new await temp will never be reached + const unreachableTemp = + tempModState.status === TEMPERATURE_AT_TARGET && + tempModState.targetTemperature !== temperature + + if (unreachableTemp || tempModState.status === TEMPERATURE_DEACTIVATED) { + return { errors: [errorCreators.missingTemperatureStep()] } + } + + const moduleType = invariantContext.moduleEntities[module]?.type + const params = { module, temperature } + switch (moduleType) { + case TEMPDECK: + return { + commands: [ + { + command: 'temperatureModule/awaitTemperature', + params, + }, + ], + } + + default: + console.error( + `awaitTemperature expected module ${module} to be ${TEMPDECK}, got ${moduleType}` + ) + return { errors: [errorCreators.missingModuleError()] } + } +} diff --git a/protocol-designer/src/step-generation/commandCreators/atomic/index.js b/protocol-designer/src/step-generation/commandCreators/atomic/index.js index 32242215cf6..c6a7f89a1fb 100644 --- a/protocol-designer/src/step-generation/commandCreators/atomic/index.js +++ b/protocol-designer/src/step-generation/commandCreators/atomic/index.js @@ -1,5 +1,6 @@ // @flow import aspirate from './aspirate' +import { awaitTemperature } from './awaitTemperature' import blowout from './blowout' import { deactivateTemperature } from './deactivateTemperature' import delay from './delay' @@ -14,6 +15,7 @@ import touchTip from './touchTip' export { aspirate, + awaitTemperature, blowout, deactivateTemperature, delay, diff --git a/protocol-designer/src/step-generation/commandCreators/index.js b/protocol-designer/src/step-generation/commandCreators/index.js index f2514e88d1c..283ce1361b8 100644 --- a/protocol-designer/src/step-generation/commandCreators/index.js +++ b/protocol-designer/src/step-generation/commandCreators/index.js @@ -3,6 +3,7 @@ export { transfer, mix, consolidate, distribute } from './compound' export { aspirate, + awaitTemperature, blowout, deactivateTemperature, delay, diff --git a/protocol-designer/src/step-generation/errorCreators.js b/protocol-designer/src/step-generation/errorCreators.js index 6a3ffb2d55f..7cfc7ef77d5 100644 --- a/protocol-designer/src/step-generation/errorCreators.js +++ b/protocol-designer/src/step-generation/errorCreators.js @@ -58,6 +58,14 @@ export function missingModuleError(): CommandCreatorError { } } +export function missingTemperatureStep(): CommandCreatorError { + return { + message: + 'This module is not changing temperature because it has either been deactivated or is already holding a temperature. In order to pause the protocol and wait for your module to reach a temperature, you must first use a Temperature step to tell the module to start changing to a new temperature', + type: 'MISSING_TEMPERATURE_STEP', + } +} + export function tipVolumeExceeded(args: {| actionName: string, volume: string | number, diff --git a/protocol-designer/src/step-generation/getNextRobotStateAndWarnings/index.js b/protocol-designer/src/step-generation/getNextRobotStateAndWarnings/index.js index 8dec244ffc3..4d94bbd200f 100644 --- a/protocol-designer/src/step-generation/getNextRobotStateAndWarnings/index.js +++ b/protocol-designer/src/step-generation/getNextRobotStateAndWarnings/index.js @@ -8,6 +8,7 @@ import { forDropTip } from './forDropTip' import { forPickUpTip } from './forPickUpTip' import { forEngageMagnet, forDisengageMagnet } from './magnetUpdates' import { + forAwaitTemperature, forSetTemperature, forDeactivateTemperature, } from './temperatureUpdates' @@ -70,6 +71,12 @@ function _getNextRobotStateAndWarningsSingleCommand( ) break case 'temperatureModule/awaitTemperature': + forAwaitTemperature( + command.params, + invariantContext, + robotStateAndWarnings + ) + break case 'thermocycler/setTargetBlockTemperature': case 'thermocycler/setTargetLidTemperature': case 'thermocycler/awaitBlockTemperature': diff --git a/protocol-designer/src/step-generation/getNextRobotStateAndWarnings/temperatureUpdates.js b/protocol-designer/src/step-generation/getNextRobotStateAndWarnings/temperatureUpdates.js index 1132243292b..817f2c0d6dc 100644 --- a/protocol-designer/src/step-generation/getNextRobotStateAndWarnings/temperatureUpdates.js +++ b/protocol-designer/src/step-generation/getNextRobotStateAndWarnings/temperatureUpdates.js @@ -1,11 +1,12 @@ // @flow import assert from 'assert' -import { getModuleState } from '../utils/misc' import { TEMPDECK, TEMPERATURE_APPROACHING_TARGET, TEMPERATURE_DEACTIVATED, + TEMPERATURE_AT_TARGET, } from '../../constants' +import { getModuleState } from '../utils/misc' import type { TemperatureParams, ModuleOnlyParams, @@ -40,6 +41,27 @@ export function forSetTemperature( ) } +export function forAwaitTemperature( + params: TemperatureParams, + invariantContext: InvariantContext, + robotStateAndWarnings: RobotStateAndWarnings +): void { + const { robotState } = robotStateAndWarnings + const { module, temperature } = params + const moduleState = getModuleState(robotState, module) + + assert( + module in robotState.modules, + `forSetTemperature expected module id "${module}"` + ) + + if (moduleState.type === TEMPDECK) { + if (temperature === moduleState.targetTemperature) { + moduleState.status = TEMPERATURE_AT_TARGET + } + } +} + export function forDeactivateTemperature( params: ModuleOnlyParams, invariantContext: InvariantContext, diff --git a/protocol-designer/src/step-generation/index.js b/protocol-designer/src/step-generation/index.js index d65b7192087..3a20a82b52e 100644 --- a/protocol-designer/src/step-generation/index.js +++ b/protocol-designer/src/step-generation/index.js @@ -1,6 +1,7 @@ // @flow export { aspirate, + awaitTemperature, blowout, consolidate, distribute, diff --git a/protocol-designer/src/step-generation/test-with-flow/awaitTemperature.test.js b/protocol-designer/src/step-generation/test-with-flow/awaitTemperature.test.js new file mode 100644 index 00000000000..04a5409b0a1 --- /dev/null +++ b/protocol-designer/src/step-generation/test-with-flow/awaitTemperature.test.js @@ -0,0 +1,150 @@ +// @flow +import { + TEMPERATURE_AT_TARGET, + TEMPERATURE_APPROACHING_TARGET, + TEMPERATURE_DEACTIVATED, +} from '../../constants' +import { awaitTemperature } from '../commandCreators/atomic/awaitTemperature' +import { + getStateAndContextTempMagModules, + robotWithStatusAndTemp, +} from './fixtures' + +describe('awaitTemperature', () => { + const temperatureModuleId = 'temperatureModuleId' + const thermocyclerId = 'thermocyclerId' + const commandCreatorFnName = 'awaitTemperature' + const prevRobotTemp = 42 + + const missingModuleError = { + errors: [{ message: expect.any(String), type: 'MISSING_MODULE' }], + } + const missingTemperatureStep = { + errors: [{ message: expect.any(String), type: 'MISSING_TEMPERATURE_STEP' }], + } + + let invariantContext + let robotState + + beforeEach(() => { + const stateAndContext = getStateAndContextTempMagModules({ + temperatureModuleId, + thermocyclerId, + }) + invariantContext = stateAndContext.invariantContext + robotState = stateAndContext.robotState + }) + + test('temperature module id exists and temp status is approaching temp', () => { + const temperature = 20 + const args = { + module: temperatureModuleId, + temperature, + commandCreatorFnName, + } + const previousRobotState = robotWithStatusAndTemp( + robotState, + temperatureModuleId, + TEMPERATURE_APPROACHING_TARGET, + prevRobotTemp + ) + + const expected = { + commands: [ + { + command: 'temperatureModule/awaitTemperature', + params: { + module: temperatureModuleId, + temperature: 20, + }, + }, + ], + } + const result = awaitTemperature(args, invariantContext, previousRobotState) + expect(result).toEqual(expected) + }) + test('returns missing module error when module id does not exist', () => { + const temperature = 42 + const args = { + module: 'someNonexistentModuleId', + temperature, + commandCreatorFnName, + } + + const result = awaitTemperature(args, invariantContext, robotState) + expect(result).toEqual(missingModuleError) + }) + test('returns missing module error when module id is null', () => { + const temperature = 42 + const args = { + module: null, + temperature, + commandCreatorFnName, + } + + const result = awaitTemperature(args, invariantContext, robotState) + expect(result).toEqual(missingModuleError) + }) + test('returns awaitTemperature command creator when temperature module already at target temp and awaiting that same temp', () => { + const temperature = 42 + const args = { + module: temperatureModuleId, + temperature, + commandCreatorFnName, + } + const previousRobotState = robotWithStatusAndTemp( + robotState, + temperatureModuleId, + TEMPERATURE_AT_TARGET, + prevRobotTemp + ) + const expected = { + commands: [ + { + command: 'temperatureModule/awaitTemperature', + params: { + module: temperatureModuleId, + temperature: 42, + }, + }, + ], + } + const result = awaitTemperature(args, invariantContext, previousRobotState) + expect(result).toEqual(expected) + }) + test('returns missing temperature step error when temperature module already at target temp and awaiting different temp', () => { + const temperature = 80 + const args = { + module: temperatureModuleId, + temperature, + commandCreatorFnName, + } + + const previousRobotState = robotWithStatusAndTemp( + robotState, + temperatureModuleId, + TEMPERATURE_AT_TARGET, + prevRobotTemp + ) + + const result = awaitTemperature(args, invariantContext, previousRobotState) + expect(result).toEqual(missingTemperatureStep) + }) + test('returns missing temperature step error when prev temp state is DEACTIVATED', () => { + const temperature = 80 + const args = { + module: temperatureModuleId, + temperature, + commandCreatorFnName, + } + const previousRobotState = robotWithStatusAndTemp( + robotState, + temperatureModuleId, + TEMPERATURE_DEACTIVATED, + prevRobotTemp + ) + + const result = awaitTemperature(args, invariantContext, previousRobotState) + expect(result).toEqual(missingTemperatureStep) + }) +}) diff --git a/protocol-designer/src/step-generation/test-with-flow/fixtures/robotStateFixtures.js b/protocol-designer/src/step-generation/test-with-flow/fixtures/robotStateFixtures.js index 762337687f4..2a29b379ce4 100644 --- a/protocol-designer/src/step-generation/test-with-flow/fixtures/robotStateFixtures.js +++ b/protocol-designer/src/step-generation/test-with-flow/fixtures/robotStateFixtures.js @@ -1,4 +1,5 @@ // @flow +import cloneDeep from 'lodash/cloneDeep' import mapValues from 'lodash/mapValues' import { getLabwareDefURI } from '@opentrons/shared-data' import { @@ -17,6 +18,8 @@ import { SPAN7_8_10_11_SLOT, TEMPDECK, THERMOCYCLER, + TEMPERATURE_APPROACHING_TARGET, + TEMPERATURE_AT_TARGET, TEMPERATURE_DEACTIVATED, } from '../../../constants' import { @@ -274,3 +277,21 @@ export const getStateAndContextTempMagModules = ({ } return { invariantContext, robotState } } + +export const robotWithStatusAndTemp = ( + robotState: RobotState, + temperatureModuleId: string, + status: + | typeof TEMPERATURE_AT_TARGET + | typeof TEMPERATURE_APPROACHING_TARGET + | typeof TEMPERATURE_DEACTIVATED, + targetTemperature: number | null +): RobotState => { + const robot = cloneDeep(robotState) + robot.modules[temperatureModuleId].moduleState = { + type: TEMPDECK, + targetTemperature, + status, + } + return robot +} diff --git a/protocol-designer/src/step-generation/test-with-flow/temperatureUpdates.test.js b/protocol-designer/src/step-generation/test-with-flow/temperatureUpdates.test.js index 7fa21a0a440..e99a53609c3 100644 --- a/protocol-designer/src/step-generation/test-with-flow/temperatureUpdates.test.js +++ b/protocol-designer/src/step-generation/test-with-flow/temperatureUpdates.test.js @@ -1,67 +1,69 @@ // @flow -import cloneDeep from 'lodash/cloneDeep' -import { makeImmutableStateUpdater } from './utils/makeImmutableStateUpdater' -import { makeContext, getInitialRobotStateStandard } from './fixtures' +import { + TEMPERATURE_DEACTIVATED, + TEMPERATURE_APPROACHING_TARGET, + TEMPERATURE_AT_TARGET, +} from '../../constants' import { forSetTemperature as _forSetTemperature, forDeactivateTemperature as _forDeactivateTemperature, + forAwaitTemperature as _forAwaitTemperature, } from '../getNextRobotStateAndWarnings/temperatureUpdates' +import { makeImmutableStateUpdater } from './utils/makeImmutableStateUpdater' import { - TEMPERATURE_DEACTIVATED, - TEMPERATURE_APPROACHING_TARGET, -} from '../../constants' -import type { RobotState } from '../types' + getStateAndContextTempMagModules, + robotWithStatusAndTemp, +} from './fixtures/robotStateFixtures' const forSetTemperature = makeImmutableStateUpdater(_forSetTemperature) const forDeactivateTemperature = makeImmutableStateUpdater( _forDeactivateTemperature ) +const forAwaitTemperature = makeImmutableStateUpdater(_forAwaitTemperature) -function createRobotWithTemp(robot: RobotState, temperature: number) { - const robotWithTemp = cloneDeep(robot) - robotWithTemp.modules[moduleId].moduleState = { - type: 'tempdeck', - targetTemperature: temperature, - status: TEMPERATURE_APPROACHING_TARGET, - } - return robotWithTemp -} - -const moduleId = 'temperatureModuleId' -const slot = '3' +const temperatureModuleId = 'temperatureModuleId' +const thermocyclerId = 'thermocyclerId' const temperature = 45 -let invariantContext, deactivatedRobot, robotWithTemp +let invariantContext, deactivatedRobot, robotWithTemp, robotState + beforeEach(() => { - invariantContext = makeContext() - invariantContext.moduleEntities[moduleId] = { - id: moduleId, - type: 'tempdeck', - model: 'GEN1', - } - - deactivatedRobot = getInitialRobotStateStandard(invariantContext) - deactivatedRobot.modules[moduleId] = { - slot, - moduleState: { - type: 'tempdeck', - targetTemperature: null, - status: TEMPERATURE_DEACTIVATED, - }, - } - robotWithTemp = createRobotWithTemp(deactivatedRobot, temperature) + const stateAndContext = getStateAndContextTempMagModules({ + temperatureModuleId, + thermocyclerId, + }) + invariantContext = stateAndContext.invariantContext + robotState = stateAndContext.robotState + + deactivatedRobot = robotWithStatusAndTemp( + robotState, + temperatureModuleId, + TEMPERATURE_DEACTIVATED, + null + ) + robotWithTemp = robotWithStatusAndTemp( + robotState, + temperatureModuleId, + TEMPERATURE_APPROACHING_TARGET, + temperature + ) }) describe('forSetTemperature', () => { test('module status is set to approaching and temp is set to target', () => { const params = { - module: moduleId, + module: temperatureModuleId, temperature: temperature, } const result = forSetTemperature(params, invariantContext, deactivatedRobot) expect(result).toEqual({ - robotState: robotWithTemp, + robotState: robotWithStatusAndTemp( + deactivatedRobot, + temperatureModuleId, + TEMPERATURE_APPROACHING_TARGET, + temperature + ), warnings: [], }) }) @@ -69,16 +71,29 @@ describe('forSetTemperature', () => { test('module temp is changed to new target temp when already active', () => { const newTemperature = 55 const params = { - module: moduleId, + module: temperatureModuleId, temperature: newTemperature, } - const robotWithNewTemp = createRobotWithTemp(robotWithTemp, newTemperature) - const result = forSetTemperature(params, invariantContext, robotWithTemp) + const result = forSetTemperature( + params, + invariantContext, + robotWithStatusAndTemp( + deactivatedRobot, + temperatureModuleId, + TEMPERATURE_APPROACHING_TARGET, + temperature + ) + ) expect(result).toEqual({ warnings: [], - robotState: robotWithNewTemp, + robotState: robotWithStatusAndTemp( + deactivatedRobot, + temperatureModuleId, + TEMPERATURE_APPROACHING_TARGET, + newTemperature + ), }) }) }) @@ -86,7 +101,7 @@ describe('forSetTemperature', () => { describe('forDeactivateTemperature', () => { test('module status is deactivated and no temperature is set', () => { const params = { - module: moduleId, + module: temperatureModuleId, } const result = forDeactivateTemperature( @@ -103,7 +118,7 @@ describe('forDeactivateTemperature', () => { test('no effect when temp module is not active', () => { const params = { - module: moduleId, + module: temperatureModuleId, } const result = forDeactivateTemperature( @@ -118,3 +133,61 @@ describe('forDeactivateTemperature', () => { }) }) }) + +describe('forAwaitTemperature', () => { + ;[TEMPERATURE_AT_TARGET, TEMPERATURE_APPROACHING_TARGET].forEach(status => { + test(`update status to 'at target' when previous status is ${status} and the given target temp matches the previous target temp`, () => { + const params = { + module: temperatureModuleId, + temperature: temperature, + } + + const prevRobotState = robotWithStatusAndTemp( + deactivatedRobot, + temperatureModuleId, + status, + temperature + ) + + const robotAtTargetTemp = robotWithStatusAndTemp( + robotWithTemp, + temperatureModuleId, + TEMPERATURE_AT_TARGET, + temperature + ) + + const result = forAwaitTemperature( + params, + invariantContext, + prevRobotState + ) + + expect(result).toEqual({ + robotState: robotAtTargetTemp, + warnings: [], + }) + }) + }) + + test(`keep status at 'appraoching target temperature' when actively approaching target`, () => { + const params = { + module: temperatureModuleId, + temperature: 55, + } + const robotAtNonTargetTemp = robotWithStatusAndTemp( + deactivatedRobot, + temperatureModuleId, + TEMPERATURE_APPROACHING_TARGET, + temperature + ) + const result = forAwaitTemperature( + params, + invariantContext, + robotAtNonTargetTemp + ) + expect(result).toEqual({ + robotState: robotAtNonTargetTemp, + warnings: [], + }) + }) +}) diff --git a/protocol-designer/src/step-generation/types.js b/protocol-designer/src/step-generation/types.js index c6898f9e104..53f0f9c44f3 100644 --- a/protocol-designer/src/step-generation/types.js +++ b/protocol-designer/src/step-generation/types.js @@ -167,6 +167,13 @@ export type PauseArgs = {| |}, |} +export type AwaitTemperatureArgs = {| + module: string | null, + commandCreatorFnName: 'awaitTemperature', + temperature: number, + message?: string, +|} + export type EngageMagnetArgs = {| ...EngageMagnetParams, module: string | null, @@ -203,6 +210,7 @@ export type CommandCreatorArgs = | EngageMagnetArgs | DisengageMagnetArgs | SetTemperatureArgs + | AwaitTemperatureArgs | DeactivateTemperatureArgs /** tips are numbered 0-7. 0 is the furthest to the back of the robot. @@ -277,6 +285,7 @@ export type ErrorType = | 'PIPETTE_DOES_NOT_EXIST' | 'PIPETTE_VOLUME_EXCEEDED' | 'TIP_VOLUME_EXCEEDED' + | 'MISSING_TEMPERATURE_STEP' export type CommandCreatorError = {| message: string, diff --git a/protocol-designer/src/step-generation/utils/misc.js b/protocol-designer/src/step-generation/utils/misc.js index 548b034aabd..5434aa33010 100644 --- a/protocol-designer/src/step-generation/utils/misc.js +++ b/protocol-designer/src/step-generation/utils/misc.js @@ -323,5 +323,5 @@ export function getModuleState( robotState: RobotState, module: string ): $PropertyType { - return robotState.modules[module].moduleState + return robotState.modules[module]?.moduleState } diff --git a/protocol-designer/src/steplist/formLevel/stepFormToArgs/pauseFormToArgs.js b/protocol-designer/src/steplist/formLevel/stepFormToArgs/pauseFormToArgs.js index 08b01834ff3..19e695b0a56 100644 --- a/protocol-designer/src/steplist/formLevel/stepFormToArgs/pauseFormToArgs.js +++ b/protocol-designer/src/steplist/formLevel/stepFormToArgs/pauseFormToArgs.js @@ -1,36 +1,58 @@ // @flow -import { PAUSE_UNTIL_TIME, PAUSE_UNTIL_TEMP } from '../../../constants' +import { + PAUSE_UNTIL_TIME, + PAUSE_UNTIL_TEMP, + PAUSE_UNTIL_RESUME, +} from '../../../constants' import type { FormData } from '../../../form-types' -import type { PauseArgs } from '../../../step-generation' +import type { AwaitTemperatureArgs, PauseArgs } from '../../../step-generation' -const pauseFormToArgs = (formData: FormData): PauseArgs => { +const pauseFormToArgs = ( + formData: FormData +): PauseArgs | AwaitTemperatureArgs | null => { const hours = parseFloat(formData['pauseHour']) || 0 const minutes = parseFloat(formData['pauseMinute']) || 0 const seconds = parseFloat(formData['pauseSecond']) || 0 const totalSeconds = hours * 3600 + minutes * 60 + seconds const temperature = parseFloat(formData['pauseTemperature']) - - let wait = true - if (formData['pauseForAmountOfTime'] === PAUSE_UNTIL_TEMP) { - wait = temperature // TODO: differentiate between seconds and temperature in step generation - } else if (formData['pauseForAmountOfTime'] === PAUSE_UNTIL_TIME) { - wait = totalSeconds - } - const message = formData['pauseMessage'] || '' - return { - commandCreatorFnName: 'delay', - name: `Pause ${formData.id}`, // TODO real name for steps - description: 'description would be here 2018-03-01', // TODO get from form - wait, - message, - meta: { - hours, - minutes, - seconds, - }, + switch (formData.pauseForAmountOfTime) { + case PAUSE_UNTIL_TEMP: + return { + commandCreatorFnName: 'awaitTemperature', + temperature, + module: formData.moduleId, + } + case PAUSE_UNTIL_TIME: + return { + commandCreatorFnName: 'delay', + name: `Pause ${formData.id}`, // TODO real name for steps + description: formData.description || '', // TODO get from form + wait: totalSeconds, + message, + meta: { + hours, + minutes, + seconds, + }, + } + case PAUSE_UNTIL_RESUME: + return { + commandCreatorFnName: 'delay', + name: `Pause ${formData.id}`, // TODO real name for steps + description: formData.description || '', // TODO get from form + wait: true, + message, + meta: { + hours, + minutes, + seconds, + }, + } + default: + return null } } diff --git a/protocol-designer/src/steplist/formLevel/stepFormToArgs/test/pauseFormToArgs.test.js b/protocol-designer/src/steplist/formLevel/stepFormToArgs/test/pauseFormToArgs.test.js new file mode 100644 index 00000000000..0631be6a425 --- /dev/null +++ b/protocol-designer/src/steplist/formLevel/stepFormToArgs/test/pauseFormToArgs.test.js @@ -0,0 +1,72 @@ +import { + PAUSE_UNTIL_TEMP, + PAUSE_UNTIL_RESUME, + PAUSE_UNTIL_TIME, +} from '../../../../constants' +import pauseFormToArgs from '../pauseFormToArgs' + +describe('pauseFormToArgs', () => { + test('returns awaitTemperature command creator when form specifies pause until temp', () => { + const formData = { + stepType: 'pause', + id: 'test_id', + pauseForAmountOfTime: PAUSE_UNTIL_TEMP, + pauseTemperature: '20', + moduleId: 'some_id', + } + const expected = { + commandCreatorFnName: 'awaitTemperature', + temperature: 20, + module: 'some_id', + } + expect(pauseFormToArgs(formData)).toEqual(expected) + }) + test('returns delay command creator when form specifies pause until resume', () => { + const formData = { + stepType: 'pause', + id: 'test_id', + pauseForAmountOfTime: PAUSE_UNTIL_RESUME, + description: 'some description', + pauseMessage: 'some message', + } + const expected = { + commandCreatorFnName: 'delay', + name: `Pause ${formData.id}`, + description: 'some description', + wait: true, + message: 'some message', + meta: { + hours: 0, + minutes: 0, + seconds: 0, + }, + } + expect(pauseFormToArgs(formData)).toEqual(expected) + }) + + test('returns delay command creator when form specifies pause until time', () => { + const formData = { + stepType: 'pause', + id: 'test_id', + pauseForAmountOfTime: PAUSE_UNTIL_TIME, + description: 'some description', + pauseMessage: 'some message', + pauseHour: 1, + pauseMinute: 20, + pauseSecond: 5, + } + const expected = { + commandCreatorFnName: 'delay', + name: `Pause ${formData.id}`, + description: 'some description', + wait: 3600 + 20 * 60 + 5, + message: 'some message', + meta: { + hours: 1, + minutes: 20, + seconds: 5, + }, + } + expect(pauseFormToArgs(formData)).toEqual(expected) + }) +})