From 41a5a39a959b9a1bed3f9e96e1287385c1647075 Mon Sep 17 00:00:00 2001 From: Sarah Breen Date: Fri, 29 Jul 2022 10:54:30 -0400 Subject: [PATCH] feat(app): adding support for v6 commands in run log (#11254) fix #11247 Co-authored-by: Jethary Rader --- .../assets/localization/en/run_details.json | 13 +- .../ProtocolRun/RunLogProtocolSetupInfo.tsx | 32 ++- .../Devices/ProtocolRun/StepText.tsx | 173 ++++++++++++--- .../ProtocolRun/__tests__/StepText.test.tsx | 208 ++++++++++++++++++ robot-server/simulators/test.json | 2 +- 5 files changed, 389 insertions(+), 39 deletions(-) diff --git a/app/src/assets/localization/en/run_details.json b/app/src/assets/localization/en/run_details.json index a785feb6658..0c27a4dc232 100644 --- a/app/src/assets/localization/en/run_details.json +++ b/app/src/assets/localization/en/run_details.json @@ -44,6 +44,7 @@ "cancel_run_module_info": "Additionally, any hardware modules used within the protocol will remain active and maintain their current states until deactivated.", "protocol_setup": "Protocol Setup", "load_pipette_protocol_setup": "Load {{pipette_name}} in {{mount_name}} Mount", + "load_liquids_info_protocol_setup": "Load {{liquid}} into {{labware}}", "load_modules_protocol_setup": "Load {{module}} in Slot {{slot_name}}", "load_modules_protocol_setup_plural": "Load {{module}}", "load_labware_info_protocol_setup_no_module": "Load {{labware_loadname}} v{{labware_version}} in Slot {{slot_number}}", @@ -128,5 +129,15 @@ "latching_hs_latch": "Latching labware on Heater-Shaker", "deactivate_hs_shake": "Deactivating shaker", "wait_for_duration": "Pause for {{seconds}} seconds", - "tc_run_profile_steps": "temperature: {{celsius}}°C, seconds: {{seconds}}" + "tc_run_profile_steps": "temperature: {{celsius}}°C, seconds: {{seconds}}", + "aspirate": "Aspirating {{volume}} uL from {{well_name}} of {{labware}} in {{labware_location}} at {{flow_rate}} uL/sec", + "dispense": "Dispensing {{volume}} uL into {{well_name}} of {{labware}} in {{labware_location}} at {{flow_rate}} uL/sec", + "blowout": "Blowing out at {{well_name}} of {{labware}} in {{labware_location}} at {{flow_rate}} uL/sec", + "touch_tip": "Touching tip", + "move_to_slot": "Moving to {{slot_name}}", + "move_to_well": "Moving to {{well_name}} of {{labware}} in {{labware_location}}", + "move_relative": "Moving {{distance}} mm along {{axis}} axis", + "move_to_coordinates": "Moving to (X: {{x}}, Y: {{y}}, Z: {{z}})", + "home_gantry": "Homing all gantry, pipette, and plunger axes", + "save_position": "Saving position" } diff --git a/app/src/organisms/Devices/ProtocolRun/RunLogProtocolSetupInfo.tsx b/app/src/organisms/Devices/ProtocolRun/RunLogProtocolSetupInfo.tsx index 685bde166f3..97a370e49b9 100644 --- a/app/src/organisms/Devices/ProtocolRun/RunLogProtocolSetupInfo.tsx +++ b/app/src/organisms/Devices/ProtocolRun/RunLogProtocolSetupInfo.tsx @@ -2,10 +2,18 @@ import * as React from 'react' import { Trans, useTranslation } from 'react-i18next' import { SPACING } from '@opentrons/components' -import { getModuleDisplayName } from '@opentrons/shared-data' +import { + getModuleDisplayName, + getLabwareDisplayName, +} from '@opentrons/shared-data' +import { parseLiquidsInLoadOrder } from '@opentrons/api-client' import { StyledText } from '../../../atoms/text' -import { useProtocolDetailsForRun, useRunPipetteInfoByMount } from '../hooks' +import { + useProtocolDetailsForRun, + useRunPipetteInfoByMount, + useLabwareRenderInfoForRunById, +} from '../hooks' import type { RunCommandSummary } from '@opentrons/api-client' import type { Mount } from '@opentrons/components' @@ -26,6 +34,7 @@ export const RunLogProtocolSetupInfo = ({ const { t } = useTranslation('run_details') const { protocolData } = useProtocolDetailsForRun(runId) const protocolPipetteData = useRunPipetteInfoByMount(robotName, runId) + const labwareRenderInfoById = useLabwareRenderInfoForRunById(runId) if (protocolData == null) return null if (setupCommand === undefined) return null @@ -123,6 +132,25 @@ export const RunLogProtocolSetupInfo = ({ }} /> ) + } else if (setupCommand.commandType === 'loadLiquid') { + const liquidInfo = parseLiquidsInLoadOrder() + const { liquidId, labwareId } = setupCommand.params + const liquidDisplayName = liquidInfo.find( + liquid => liquid.liquidId === liquidId + )?.displayName + setupCommandText = ( + + ) } return ( diff --git a/app/src/organisms/Devices/ProtocolRun/StepText.tsx b/app/src/organisms/Devices/ProtocolRun/StepText.tsx index 67d76a27dee..3e369e7459b 100644 --- a/app/src/organisms/Devices/ProtocolRun/StepText.tsx +++ b/app/src/organisms/Devices/ProtocolRun/StepText.tsx @@ -1,8 +1,7 @@ import * as React from 'react' -import { Trans, useTranslation } from 'react-i18next' +import { useTranslation } from 'react-i18next' import { Flex, ALIGN_CENTER, SPACING, TYPOGRAPHY } from '@opentrons/components' import { getLabwareDisplayName } from '@opentrons/shared-data' - import { StyledText } from '../../../atoms/text' import { getLabwareLocation } from '../ProtocolRun/utils/getLabwareLocation' import { @@ -72,24 +71,18 @@ export function StepText(props: Props): JSX.Element | null { if (!('slotName' in labwareLocation)) { throw new Error('expected tip rack to be in a slot') } - messageNode = ( - - ) + messageNode = t('drop_tip', { + well_name: wellName, + labware: + labwareId === TRASH_ID + ? 'Opentrons Fixed Trash' + : getLabwareDisplayName( + protocolData.labwareDefinitions[ + protocolData.labware[labwareId].definitionId + ] + ), + labware_location: labwareLocation.slotName, + }) break } case 'pickUpTip': { @@ -101,19 +94,13 @@ export function StepText(props: Props): JSX.Element | null { if (!('slotName' in labwareLocation)) { throw new Error('expected tip rack to be in a slot') } - messageNode = ( - - ) + messageNode = t('pickup_tip', { + well_name: wellName, + labware: getLabwareDisplayName( + labwareRenderInfoById[labwareId].labwareDef + ), + labware_location: labwareLocation.slotName, + }) break } case 'pause': @@ -123,7 +110,8 @@ export function StepText(props: Props): JSX.Element | null { } case 'loadLabware': case 'loadPipette': - case 'loadModule': { + case 'loadModule': + case 'loadLiquid': { messageNode = ( { }) getByText('Pause for 60 seconds') }) + it('renders correct command text for aspirate', () => { + const labwareId = 'labwareId' + when(mockGetLabwareDisplayName) + .calledWith('fake_def' as any) + .mockReturnValue('fake_display_name') + when(mockGetLabwareLocation) + .calledWith(labwareId, []) + .mockReturnValue({ slotName: 'fake_labware_location' }) + mockUseLabwareRenderInfoForRunById.mockReturnValue({ + labwareId: { + labwareDef: 'fake_def', + }, + } as any) + const { getByText } = render({ + robotName: ROBOT_NAME, + runId: RUN_ID, + analysisCommand: null, + runCommand: { + ...MOCK_COMMAND_SUMMARY, + commandType: 'aspirate', + params: { + volume: 100, + flowRate: 130, + wellName: 'wellName', + labwareId: 'labwareId', + }, + }, + }) + getByText( + 'Aspirating 100 uL from wellName of fake_display_name in fake_labware_location at 130 uL/sec' + ) + }) + it('renders correct command text for dispense', () => { + const labwareId = 'labwareId' + when(mockGetLabwareDisplayName) + .calledWith('fake_def' as any) + .mockReturnValue('fake_display_name') + when(mockGetLabwareLocation) + .calledWith(labwareId, []) + .mockReturnValue({ slotName: 'fake_labware_location' }) + mockUseLabwareRenderInfoForRunById.mockReturnValue({ + labwareId: { + labwareDef: 'fake_def', + }, + } as any) + const { getByText } = render({ + robotName: ROBOT_NAME, + runId: RUN_ID, + analysisCommand: null, + runCommand: { + ...MOCK_COMMAND_SUMMARY, + commandType: 'dispense', + params: { + volume: 100, + flowRate: 130, + wellName: 'wellName', + labwareId: 'labwareId', + }, + }, + }) + getByText( + 'Dispensing 100 uL into wellName of fake_display_name in fake_labware_location at 130 uL/sec' + ) + }) + it('renders correct command text for blowout', () => { + const labwareId = 'labwareId' + when(mockGetLabwareDisplayName) + .calledWith('fake_def' as any) + .mockReturnValue('fake_display_name') + when(mockGetLabwareLocation) + .calledWith(labwareId, []) + .mockReturnValue({ slotName: 'fake_labware_location' }) + mockUseLabwareRenderInfoForRunById.mockReturnValue({ + labwareId: { + labwareDef: 'fake_def', + }, + } as any) + const { getByText } = render({ + robotName: ROBOT_NAME, + runId: RUN_ID, + analysisCommand: null, + runCommand: { + ...MOCK_COMMAND_SUMMARY, + commandType: 'blowout', + params: { + flowRate: 130, + wellName: 'wellName', + labwareId: 'labwareId', + }, + }, + }) + getByText( + 'Blowing out at wellName of fake_display_name in fake_labware_location at 130 uL/sec' + ) + }) + it('renders correct command text for touchTip', () => { + const { getByText } = render({ + robotName: ROBOT_NAME, + runId: RUN_ID, + analysisCommand: null, + runCommand: { + ...MOCK_COMMAND_SUMMARY, + commandType: 'touchTip', + }, + }) + getByText('Touching tip') + }) + it('renders correct command text for moveToSlot', () => { + const { getByText } = render({ + robotName: ROBOT_NAME, + runId: RUN_ID, + analysisCommand: null, + runCommand: { + ...MOCK_COMMAND_SUMMARY, + commandType: 'moveToSlot', + params: { slotName: 'slot 5' }, + }, + }) + getByText('Moving to slot 5') + }) + it('renders correct command text for moveToWell', () => { + const labwareId = 'labwareId' + when(mockGetLabwareDisplayName) + .calledWith('fake_def' as any) + .mockReturnValue('fake_display_name') + when(mockGetLabwareLocation) + .calledWith(labwareId, []) + .mockReturnValue({ slotName: 'fake_labware_location' }) + mockUseLabwareRenderInfoForRunById.mockReturnValue({ + labwareId: { + labwareDef: 'fake_def', + }, + } as any) + const { getByText } = render({ + robotName: ROBOT_NAME, + runId: RUN_ID, + analysisCommand: null, + runCommand: { + ...MOCK_COMMAND_SUMMARY, + commandType: 'moveToWell', + params: { + wellName: 'wellName', + labwareId: 'labwareId', + }, + }, + }) + getByText( + 'Moving to wellName of fake_display_name in fake_labware_location' + ) + }) + it('renders correct command text for moveRelative', () => { + const { getByText } = render({ + robotName: ROBOT_NAME, + runId: RUN_ID, + analysisCommand: null, + runCommand: { + ...MOCK_COMMAND_SUMMARY, + commandType: 'moveRelative', + params: { distance: '4', axis: 'y' }, + }, + }) + getByText('Moving 4 mm along y axis') + }) + it('renders correct command text for moveToCoordinates', () => { + const { getByText } = render({ + robotName: ROBOT_NAME, + runId: RUN_ID, + analysisCommand: null, + runCommand: { + ...MOCK_COMMAND_SUMMARY, + commandType: 'moveToCoordinates', + params: { coordinates: { x: '4', y: '5', z: '2' } }, + }, + }) + getByText('Moving to (X: 4, Y: 5, Z: 2)') + }) + + it('renders correct command text for home', () => { + const { getByText } = render({ + robotName: ROBOT_NAME, + runId: RUN_ID, + analysisCommand: null, + runCommand: { + ...MOCK_COMMAND_SUMMARY, + commandType: 'home', + params: { + axes: ['x', 'y', 'leftZ', 'rightZ', 'leftPLunger', 'rightPlunger'], + }, + }, + }) + getByText('Homing all gantry, pipette, and plunger axes') + }) + + it('renders correct command text for savingPosition', () => { + const { getByText } = render({ + robotName: ROBOT_NAME, + runId: RUN_ID, + analysisCommand: null, + runCommand: { + ...MOCK_COMMAND_SUMMARY, + commandType: 'savePosition', + params: { + pipetteId: 'pipetteId', + }, + }, + }) + getByText('Saving position') + }) }) diff --git a/robot-server/simulators/test.json b/robot-server/simulators/test.json index 0a75ffa5dc6..6c08374a67a 100644 --- a/robot-server/simulators/test.json +++ b/robot-server/simulators/test.json @@ -54,4 +54,4 @@ } ] } -} \ No newline at end of file +}