Skip to content

Commit

Permalink
feat(app): update protocol detail and protocol setup status for deck …
Browse files Browse the repository at this point in the history
…configuration

ODD: Render the correct modules and deck status taking into account location conflicts, missing
hardware, and missing calibrations. Render this status in both ProtocolDetails (chipText) and
ProtocolSetup (Modules & deck) pages. Extend hooks to look for conflicting or missing hardware
  • Loading branch information
ncdiehl11 committed Oct 26, 2023
1 parent 0845ba5 commit bd7d93f
Show file tree
Hide file tree
Showing 7 changed files with 345 additions and 95 deletions.
8 changes: 5 additions & 3 deletions app/src/assets/localization/en/device_details.json
Original file line number Diff line number Diff line change
Expand Up @@ -77,11 +77,13 @@
"magdeck_gen1_height": "Height: {{height}}",
"magdeck_gen2_height": "Height: {{height}} mm",
"max_engage_height": "Max Engage Height",
"missing_both": "missing hardware",
"missing_hardware": "missing hardware",
"missing_module_plural": "missing {{count}} modules",
"missing_module": "missing {{num}} module",
"missing_pipette": "missing {{num}} pipette",
"missing_pipettes_plural": "missing {{count}} pipettes",
"missing_instrument": "missing {{num}} instrument",
"missing_instruments_plural": "missing {{count}} instruments",
"missing_fixture": "missing {{num}} fixture",
"missing_fixtures_plural": "missing {{count}} fixtures",
"module_actions_unavailable": "Module actions unavailable while protocol is running",
"module_calibration_required_no_pipette_attached": "Module calibration required. Attach a pipette before running module calibration.",
"module_calibration_required_update_pipette_FW": "Update pipette firmware before proceeding with required module calibration.",
Expand Down
10 changes: 8 additions & 2 deletions app/src/assets/localization/en/protocol_setup.json
Original file line number Diff line number Diff line change
Expand Up @@ -62,14 +62,19 @@
"feedback_form_link": "Let us know!",
"fixture_name": "fixture",
"fixture": "Fixture",
"fixtures_connected_plural": "{{count}} fixtures attached",
"fixture_connected": "{{count}} fixture attached",
"get_labware_offset_data": "Get Labware Offset Data",
"hardware_missing": "Hardware missing",
"heater_shaker_extra_attention": "Use latch controls for easy placement of labware.",
"heater_shaker_labware_list_view": "To add labware, use the toggle to control the latch",
"how_offset_data_works": "How labware offsets work",
"initial_liquids_num_plural": "{{count}} initial liquids",
"initial_liquids_num": "{{count}} initial liquid",
"initial_location": "Initial Location",
"install_modules_and_fixtures": "Install the required modules and power them on. Install the required fixtures and review the deck configuration.",
"instrument_calibrations_missing_plural": "Missing {{num}} calibrations",
"instrument_calibration_missing": "Missing {{num}} calibration",
"instruments_connected_plural": "{{count}} instruments attached",
"instruments_connected": "{{count}} instrument attached",
"instruments": "Instruments",
Expand Down Expand Up @@ -135,11 +140,12 @@
"modules": "Modules",
"mount_title": "{{mount}} MOUNT:",
"mount": "{{mount}} mount",
"multiple_fixtures_missing": "{{count}} fixtures missing",
"multiple_modules_example": "Your protocol has two Temperature Modules. The Temperature Module attached to the first port starting from the left will be related to the first Temperature Module in your protocol while the second Temperature Module loaded would be related to the Temperature Module connected to the next port to the right. If using a hub, follow the same logic with the port ordering.",
"multiple_modules_explanation": "To use more than one of the same module in a protocol, you first need to plug in the module that’s called first in your protocol to the lowest numbered USB port on the robot. Continue in the same manner with additional modules.",
"multiple_modules_help_link_title": "See How To Set Up Modules of the Same Type",
"multiple_modules_learn_more": "Learn more about using multiple modules of the same type",
"multiple_modules_missing": "Multiple modules missing",
"multiple_modules_missing": "{{count}} modules missing",
"multiple_modules_modal": "Setting up multiple modules of the same type",
"multiple_modules": "Multiple modules of the same type",
"multiple_of_most_modules": "You can use multiples of most module types within a single Python protocol by connecting and loading the modules in a specific order. The robot will initialize the matching module attached to the lowest numbered port first, regardless of what deck slot it occupies.",
Expand All @@ -149,7 +155,7 @@
"no_labware_offset_data": "no labware offset data yet",
"no_modules_or_fixtures": "No modules or fixtures are specified for this protocol.",
"no_modules_specified": "no modules are specified for this protocol.",
"no_modules_used_in_this_protocol": "No modules used in this protocol",
"no_modules_used_in_this_protocol": "No hardware used in this protocol",
"no_tiprack_loaded": "Protocol must load a tip rack",
"no_tiprack_used": "Protocol must pick up a tip",
"no_usb_connection_required": "No USB connection required",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,51 +1,72 @@
import { useTranslation } from 'react-i18next'

import { useFeatureFlag } from '../../../../redux/config'

import type { ProtocolHardware } from '../../../../pages/Protocols/hooks'

export function useHardwareStatusText(
missingProtocolHardware: ProtocolHardware[],
conflictedSlots: string[]
): string {
const { t, i18n } = useTranslation('device_details')
const enableDeckConfig = useFeatureFlag('enableDeckConfiguration')

const missingProtocolHardwareType = missingProtocolHardware.map(
hardware => hardware.hardwareType
const missingProtocolHardwareType = missingProtocolHardware.map(hardware =>
hardware.hardwareType === 'pipette' || hardware.hardwareType === 'gripper'
? 'instrument'
: hardware.hardwareType
)
const countMissingHardwareType = (hwType: 'pipette' | 'module'): number => {
const countMissingHardwareType = (
hwType: 'instrument' | 'module' | 'fixture'
): number => {
return missingProtocolHardwareType.filter(
hardwareType => hardwareType === hwType
).length
}
const countMissingPipettes = countMissingHardwareType('pipette')

const countMissingInstruments = countMissingHardwareType('instrument')
const countMissingModules = countMissingHardwareType('module')
let chipText: string = t('ready_to_run')
if (enableDeckConfig && conflictedSlots.length > 0) {
const countMissingFixtures = countMissingHardwareType('fixture')

const noHardwareMissing =
[countMissingInstruments, countMissingModules, countMissingFixtures].filter(
count => count > 0
).length === 0
const multipleHardwareTypesMissing =
[countMissingInstruments, countMissingModules, countMissingFixtures].filter(
count => count > 0
).length > 1

let chipText: string

if (noHardwareMissing) {
chipText = t('ready_to_run')
} else if (conflictedSlots.length > 0) {
chipText = t('location_conflicts')
} else if (countMissingPipettes === 0 && countMissingModules > 0) {
if (countMissingModules === 1) {
chipText = t('missing_module', {
num: countMissingModules,
})
} else {
chipText = t('missing_module_plural', {
count: countMissingModules,
})
}
} else if (countMissingPipettes > 0 && countMissingModules === 0) {
if (countMissingPipettes === 1) {
chipText = t('missing_pipette', {
num: countMissingPipettes,
})
} else if (multipleHardwareTypesMissing) {
chipText = t('missing_hardware')
} else {
// exactly one hardware type missing
if (countMissingFixtures > 0) {
chipText =
countMissingFixtures === 1
? t('missing_fixture', { num: countMissingFixtures })
: t('missing_fixtures_plural', { count: countMissingFixtures })
} else if (countMissingModules > 0) {
chipText =
countMissingModules === 1
? t('missing_module', {
num: countMissingModules,
})
: t('missing_module_plural', {
count: countMissingModules,
})
} else {
chipText = t('missing_pipettes_plural', {
count: countMissingPipettes,
})
chipText =
countMissingInstruments === 1
? t('missing_instrument', {
num: countMissingInstruments,
})
: t('missing_instruments_plural', {
count: countMissingInstruments,
})
}
} else if (countMissingPipettes > 0 && countMissingModules > 0) {
chipText = t('missing_both')
}
return i18n.format(chipText, 'capitalize')
}
22 changes: 22 additions & 0 deletions app/src/organisms/ProtocolSetupInstruments/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,3 +62,25 @@ export function getAreInstrumentsReady(

return allSpeccedPipettesReady && isExtensionMountReady
}

export function getNumUnreadyInstruments(
analysis: CompletedProtocolAnalysis,
attachedInstruments: Instruments
): number {
const speccedPipettes = analysis?.pipettes ?? []

const numUnreadySpeccedPipettes = speccedPipettes.filter(loadedPipette => {
const attachedPipetteMatch = getPipetteMatch(
loadedPipette,
attachedInstruments
)
return attachedPipetteMatch?.data.calibratedOffset?.last_modified == null
}).length

const isExtensionMountReady = getProtocolUsesGripper(analysis)
? getAttachedGripper(attachedInstruments)?.data.calibratedOffset
?.last_modified != null
: true

return numUnreadySpeccedPipettes + (isExtensionMountReady ? 0 : 1)
}
Loading

0 comments on commit bd7d93f

Please sign in to comment.