Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(app): Add addressable area command display names #14070

Merged
merged 4 commits into from
Dec 4, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions app/src/assets/localization/en/protocol_command_text.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
"move_to_coordinates": "Moving to (X: {{x}}, Y: {{y}}, Z: {{z}})",
"move_to_slot": "Moving to Slot {{slot_name}}",
"move_to_well": "Moving to well {{well_name}} of {{labware}} in {{labware_location}}",
"move_to_addressable_area": "Moving to {{addressable_area}} at {{speed}} mm/s at {{height}} mm high",
"move_to_addressable_area": "Moving to {{addressable_area}}",
"notes": "notes",
"off_deck": "off deck",
"offdeck": "offdeck",
Expand All @@ -57,12 +57,14 @@
"tc_awaiting_for_duration": "Waiting for Thermocycler profile to complete",
"tc_run_profile_steps": "temperature: {{celsius}}°C, seconds: {{seconds}}",
"tc_starting_profile": "Thermocycler starting {{repetitions}} repetitions of cycle composed of the following steps:",
"trash_bin_in_slot": "Trash Bin in {{slot_name}}",
"touch_tip": "Touching tip",
"unlatching_hs_latch": "Unlatching labware on Heater-Shaker",
"wait_for_duration": "Pausing for {{seconds}} seconds. {{message}}",
"wait_for_resume": "Pausing protocol",
"waiting_for_hs_to_reach": "Waiting for Heater-Shaker to reach target temperature",
"waiting_for_tc_block_to_reach": "Waiting for Thermocycler block to reach target temperature and holding for specified time",
"waiting_for_tc_lid_to_reach": "Waiting for Thermocycler lid to reach target temperature",
"waiting_to_reach_temp_module": "Waiting for Temperature Module to reach {{temp}}"
"waiting_to_reach_temp_module": "Waiting for Temperature Module to reach {{temp}}",
"waste_chute": "Waste Chute"
}
Original file line number Diff line number Diff line change
Expand Up @@ -6331,6 +6331,66 @@
"result": {},
"startedAt": "2023-01-31T21:53:14.615361+00:00",
"completedAt": "2023-01-31T21:53:14.616757+00:00"
},
{
"id": "aca688ed-4916-496d-aae8-ca0e6e56c47b",
"key": "e4be36b4-500c-45dd-8405-73115c80aa4c",
"commandType": "moveToAddressableArea",
"createdAt": "2023-11-29T20:31:00.780650+00:00",
"startedAt": "2023-11-29T20:31:29.248207+00:00",
"completedAt": "2023-11-29T20:31:33.972466+00:00",
"status": "succeeded",
"params": {
"forceDirect": false,
"pipetteId": "c51ddc01-2d21-4e8a-9b10-2cef033488e8",
"addressableAreaName": "1and8ChannelWasteChute",
"offset": { "x": 0, "y": 0, "z": 0 }
}
},
{
"id": "aca688ed-4916-496d-aae8-ca0e6e56c47c",
"key": "e4be36b4-500c-45dd-8405-73115c80aa44",
"commandType": "moveToAddressableArea",
"createdAt": "2023-11-29T20:31:00.780650+00:00",
"startedAt": "2023-11-29T20:31:29.248207+00:00",
"completedAt": "2023-11-29T20:31:33.972466+00:00",
"status": "succeeded",
"params": {
"forceDirect": false,
"pipetteId": "c51ddc01-2d21-4e8a-9b10-2cef033488e8",
"addressableAreaName": "fixedTrash",
"offset": { "x": 0, "y": 0, "z": 0 }
}
},
{
"id": "aca688ed-4916-496d-aae8-ca0e6e56c47d",
"key": "e4be36b4-500c-45dd-8405-73115c80aa4z",
"commandType": "moveToAddressableArea",
"createdAt": "2023-11-29T20:31:00.780650+00:00",
"startedAt": "2023-11-29T20:31:29.248207+00:00",
"completedAt": "2023-11-29T20:31:33.972466+00:00",
"status": "succeeded",
"params": {
"forceDirect": false,
"pipetteId": "c51ddc01-2d21-4e8a-9b10-2cef033488e8",
"addressableAreaName": "movableTrashD3",
"offset": { "x": 0, "y": 0, "z": 0 }
}
},
{
"id": "aca688ed-4916-496d-aae8-ca0e6e56c47e",
"key": "e4be36b4-500c-45dd-8405-73115c80aa4x",
"commandType": "moveToAddressableArea",
"createdAt": "2023-11-29T20:31:00.780650+00:00",
"startedAt": "2023-11-29T20:31:29.248207+00:00",
"completedAt": "2023-11-29T20:31:33.972466+00:00",
"status": "succeeded",
"params": {
"forceDirect": false,
"pipetteId": "c51ddc01-2d21-4e8a-9b10-2cef033488e8",
"addressableAreaName": "D3",
"offset": { "x": 0, "y": 0, "z": 0 }
}
}
],
"errors": [],
Expand Down
97 changes: 93 additions & 4 deletions app/src/organisms/CommandText/__tests__/CommandText.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
DropTipInPlaceRunTimeCommand,
FLEX_ROBOT_TYPE,
MoveToAddressableAreaRunTimeCommand,
OT2_ROBOT_TYPE,
PrepareToAspirateRunTimeCommand,
} from '@opentrons/shared-data'
import { i18n } from '../../../i18n'
Expand Down Expand Up @@ -193,26 +194,114 @@ describe('CommandText', () => {
getByText('Moving to well A1 of NEST 1 Well Reservoir 195 mL in Slot 5')
}
})
it('renders correct text for moveToAddressableArea', () => {
it('renders correct text for labware involving an addressable area slot', () => {
const { getByText } = renderWithProviders(
<CommandText
command={{
commandType: 'moveLabware',
params: {
strategy: 'usingGripper',
labwareId: mockRobotSideAnalysis.labware[2].id,
newLocation: { addressableAreaName: '5' },
},
id: 'def456',
result: { offsetId: 'fake_offset_id' },
status: 'queued',
error: null,
createdAt: 'fake_timestamp',
startedAt: null,
completedAt: null,
}}
robotSideAnalysis={mockRobotSideAnalysis}
robotType={FLEX_ROBOT_TYPE}
/>,
{
i18nInstance: i18n,
}
)[0]
getByText(
'Moving Opentrons 96 Tip Rack 300 µL using gripper from Slot 9 to Slot 5'
)
})
it('renders correct text for moveToAddressableArea for Waste Chutes', () => {
const { getByText } = renderWithProviders(
<CommandText
robotSideAnalysis={mockRobotSideAnalysis}
robotType={FLEX_ROBOT_TYPE}
command={
{
id: 'aca688ed-4916-496d-aae8-ca0e6e56c47b',
commandType: 'moveToAddressableArea',
params: {
pipetteId: 'f6d1c83c-9d1b-4d0d-9de3-e6d649739cfb',
addressableAreaName: '1and8ChannelWasteChute',
},
} as MoveToAddressableAreaRunTimeCommand
}
/>,
{ i18nInstance: i18n }
)[0]
getByText('Moving to Waste Chute')
})
it('renders correct text for moveToAddressableArea for Fixed Trash', () => {
const { getByText } = renderWithProviders(
<CommandText
robotSideAnalysis={mockRobotSideAnalysis}
robotType={OT2_ROBOT_TYPE}
command={
{
id: 'aca688ed-4916-496d-aae8-ca0e6e56c47c',
commandType: 'moveToAddressableArea',
params: {
pipetteId: 'f6d1c83c-9d1b-4d0d-9de3-e6d649739cfb',
addressableAreaName: 'fixedTrash',
},
} as MoveToAddressableAreaRunTimeCommand
}
/>,
{ i18nInstance: i18n }
)[0]
getByText('Moving to Fixed Trash')
})
it('renders correct text for moveToAddressableArea for Trash Bins', () => {
const { getByText } = renderWithProviders(
<CommandText
robotSideAnalysis={mockRobotSideAnalysis}
robotType={OT2_ROBOT_TYPE}
command={
{
id: 'aca688ed-4916-496d-aae8-ca0e6e56c47d',
commandType: 'moveToAddressableArea',
params: {
pipetteId: 'f6d1c83c-9d1b-4d0d-9de3-e6d649739cfb',
addressableAreaName: 'movableTrashD3',
},
} as MoveToAddressableAreaRunTimeCommand
}
/>,
{ i18nInstance: i18n }
)[0]
getByText('Moving to Trash Bin in D3')
})
it('renders correct text for moveToAddressableArea for slots', () => {
const { getByText } = renderWithProviders(
<CommandText
robotSideAnalysis={mockRobotSideAnalysis}
robotType={OT2_ROBOT_TYPE}
command={
{
id: 'aca688ed-4916-496d-aae8-ca0e6e56c47e',
commandType: 'moveToAddressableArea',
params: {
pipetteId: 'f6d1c83c-9d1b-4d0d-9de3-e6d649739cfb',
addressableAreaName: 'D3',
speed: 200,
minimumZHeight: 100,
},
} as MoveToAddressableAreaRunTimeCommand
}
/>,
{ i18nInstance: i18n }
)[0]
getByText('Moving to D3 at 200 mm/s at 100 mm high')
getByText('Moving to D3')
})
it('renders correct text for configureForVolume', () => {
const command = {
Expand Down
12 changes: 8 additions & 4 deletions app/src/organisms/CommandText/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { useTranslation } from 'react-i18next'
import { Flex, DIRECTION_COLUMN, SPACING } from '@opentrons/components'
import { getPipetteNameSpecs, RunTimeCommand } from '@opentrons/shared-data'
import {
getAddressableAreaDisplayName,
getLabwareName,
getLabwareDisplayLocation,
getFinalLabwareLocation,
Expand Down Expand Up @@ -235,13 +236,16 @@ export function CommandText(props: Props): JSX.Element | null {
)
}
case 'moveToAddressableArea': {
const { addressableAreaName, speed, minimumZHeight } = command.params
Copy link
Contributor Author

@mjhuff mjhuff Nov 30, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Talked w/ Brian - we don't really need to provide speed and height offsets to the user as command text. Additionally, these values aren't always present, so special casing them whenever available is superfluous work.

const addressableAreaDisplayName = getAddressableAreaDisplayName(
robotSideAnalysis,
command.id,
t
)

return (
<StyledText as="p" {...styleProps}>
{t('move_to_addressable_area', {
addressable_area: addressableAreaName,
speed: speed,
height: minimumZHeight,
addressable_area: addressableAreaDisplayName,
})}
</StyledText>
)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import type {
CompletedProtocolAnalysis,
MoveToAddressableAreaParams,
} from '@opentrons/shared-data'
import type { TFunction } from 'react-i18next'

export function getAddressableAreaDisplayName(
analysis: CompletedProtocolAnalysis,
commandId: string,
t: TFunction<'protocol_command_text'>
): string {
const addressableAreaCommand = (analysis?.commands ?? []).find(
command => command.id === commandId
)

if (
addressableAreaCommand == null ||
!('addressableAreaName' in addressableAreaCommand.params)
) {
return ''

Check warning on line 20 in app/src/organisms/CommandText/utils/getAddressableAreaDisplayName.ts

View check run for this annotation

Codecov / codecov/patch

app/src/organisms/CommandText/utils/getAddressableAreaDisplayName.ts#L20

Added line #L20 was not covered by tests
}

const addressableAreaName: MoveToAddressableAreaParams['addressableAreaName'] =
addressableAreaCommand.params.addressableAreaName

if (addressableAreaName.includes('movableTrash')) {
const slotName = getMovableTrashSlot(addressableAreaName)
return t('trash_bin_in_slot', { slot_name: slotName })
} else if (addressableAreaName.includes('WasteChute')) {
return t('waste_chute')
} else if (addressableAreaName === 'fixedTrash') return t('fixed_trash')
else return addressableAreaName
}

const getMovableTrashSlot = (addressableAreaName: string): string => {
switch (addressableAreaName) {
case 'movableTrashA1':
return 'A1'

Check warning on line 38 in app/src/organisms/CommandText/utils/getAddressableAreaDisplayName.ts

View check run for this annotation

Codecov / codecov/patch

app/src/organisms/CommandText/utils/getAddressableAreaDisplayName.ts#L38

Added line #L38 was not covered by tests
case 'movableTrashA3':
return 'A3'

Check warning on line 40 in app/src/organisms/CommandText/utils/getAddressableAreaDisplayName.ts

View check run for this annotation

Codecov / codecov/patch

app/src/organisms/CommandText/utils/getAddressableAreaDisplayName.ts#L40

Added line #L40 was not covered by tests
case 'movableTrashB1':
return 'B1'

Check warning on line 42 in app/src/organisms/CommandText/utils/getAddressableAreaDisplayName.ts

View check run for this annotation

Codecov / codecov/patch

app/src/organisms/CommandText/utils/getAddressableAreaDisplayName.ts#L42

Added line #L42 was not covered by tests
case 'movableTrashB3':
return 'B3'

Check warning on line 44 in app/src/organisms/CommandText/utils/getAddressableAreaDisplayName.ts

View check run for this annotation

Codecov / codecov/patch

app/src/organisms/CommandText/utils/getAddressableAreaDisplayName.ts#L44

Added line #L44 was not covered by tests
case 'movableTrashC1':
return 'C1'

Check warning on line 46 in app/src/organisms/CommandText/utils/getAddressableAreaDisplayName.ts

View check run for this annotation

Codecov / codecov/patch

app/src/organisms/CommandText/utils/getAddressableAreaDisplayName.ts#L46

Added line #L46 was not covered by tests
case 'movableTrashC3':
return 'C3'

Check warning on line 48 in app/src/organisms/CommandText/utils/getAddressableAreaDisplayName.ts

View check run for this annotation

Codecov / codecov/patch

app/src/organisms/CommandText/utils/getAddressableAreaDisplayName.ts#L48

Added line #L48 was not covered by tests
case 'movableTrashD1':
return 'D1'

Check warning on line 50 in app/src/organisms/CommandText/utils/getAddressableAreaDisplayName.ts

View check run for this annotation

Codecov / codecov/patch

app/src/organisms/CommandText/utils/getAddressableAreaDisplayName.ts#L50

Added line #L50 was not covered by tests
case 'movableTrashD3':
return 'D3'
default:
return ''

Check warning on line 54 in app/src/organisms/CommandText/utils/getAddressableAreaDisplayName.ts

View check run for this annotation

Codecov / codecov/patch

app/src/organisms/CommandText/utils/getAddressableAreaDisplayName.ts#L54

Added line #L54 was not covered by tests
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ export function getLabwareDisplayLocation(
return isOnDevice
? location.slotName
: t('slot', { slot_name: location.slotName })
} else if ('addressableAreaName' in location) {
return isOnDevice
? location.addressableAreaName
: t('slot', { slot_name: location.addressableAreaName })
} else if ('moduleId' in location) {
const moduleModel = getModuleModel(robotSideAnalysis, location.moduleId)
if (moduleModel == null) {
Expand Down
1 change: 1 addition & 0 deletions app/src/organisms/CommandText/utils/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from './getAddressableAreaDisplayName'
export * from './getLabwareName'
export * from './getPipetteNameOnMount'
export * from './getModuleModel'
Expand Down
Loading