@opentrons/components 0.1.7-alpha.1 → 0.2.0-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/cssModuleSideEffect.ts +22 -0
- package/lib/assets/localization/en/index.d.ts +12 -0
- package/lib/assets/localization/index.d.ts +12 -0
- package/lib/atoms/AnimationVideo/index.d.ts +13 -0
- package/lib/atoms/InlineNotification/index.d.ts +1 -1
- package/lib/atoms/ListButton/ListButtonChildren/index.d.ts +0 -1
- package/lib/atoms/StyledText/PlaceholderStyledText.d.ts +14 -0
- package/lib/atoms/StyledText/index.d.ts +1 -0
- package/lib/atoms/buttons/BasicButton.d.ts +12 -0
- package/lib/atoms/buttons/index.d.ts +1 -0
- package/lib/atoms/index.d.ts +1 -0
- package/lib/hardware-sim/BaseDeck/BaseDeck.d.ts +10 -8
- package/lib/hardware-sim/Deck/FlexTrash.d.ts +4 -1
- package/lib/hardware-sim/Deck/MoveLabwareOnDeck.d.ts +3 -2
- package/lib/hardware-sim/Deck/RobotCoordsForeignDiv.d.ts +6 -5
- package/lib/hardware-sim/Deck/RobotCoordsForeignObject.d.ts +8 -5
- package/lib/hardware-sim/Deck/RobotCoordsText.d.ts +11 -1
- package/lib/hardware-sim/Deck/RobotWorkSpace.d.ts +30 -14
- package/lib/hardware-sim/Deck/__tests__/resolveLabwareLocation.test.d.ts +1 -0
- package/lib/hardware-sim/Deck/resolveLabwareLocation.d.ts +20 -0
- package/lib/hardware-sim/DeckConfigurator/AbsorbanceReaderItem.d.ts +11 -0
- package/lib/hardware-sim/DeckConfigurator/EmptyConfigItem.d.ts +11 -0
- package/lib/hardware-sim/DeckConfigurator/FlexStackerItem.d.ts +12 -0
- package/lib/hardware-sim/DeckConfigurator/HeaterShakerItem.d.ts +11 -0
- package/lib/hardware-sim/DeckConfigurator/MagneticBlockItem.d.ts +12 -0
- package/lib/hardware-sim/DeckConfigurator/StagingAreaConfigItem.d.ts +11 -0
- package/lib/hardware-sim/DeckConfigurator/{StaticFixture.d.ts → StaticItem.d.ts} +1 -1
- package/lib/hardware-sim/DeckConfigurator/TemperatureModuleItem.d.ts +11 -0
- package/lib/hardware-sim/DeckConfigurator/ThermocyclerItem.d.ts +11 -0
- package/lib/hardware-sim/DeckConfigurator/TrashBinConfigItem.d.ts +11 -0
- package/lib/hardware-sim/DeckConfigurator/WasteChuteConfigItem.d.ts +11 -0
- package/lib/hardware-sim/DeckConfigurator/constants.d.ts +5 -8
- package/lib/hardware-sim/DeckConfigurator/index.d.ts +4 -3
- package/lib/hardware-sim/Labware/CalibrationBlockRender.d.ts +15 -0
- package/lib/hardware-sim/Labware/Labware.d.ts +7 -8
- package/lib/hardware-sim/Labware/LabwareAdapter/index.d.ts +2 -2
- package/lib/hardware-sim/Labware/LabwareRender.d.ts +46 -5
- package/lib/hardware-sim/Labware/index.d.ts +1 -0
- package/lib/hardware-sim/Labware/labwareInternals/FilledWells.d.ts +3 -4
- package/lib/hardware-sim/Labware/labwareInternals/LabwareOutline.d.ts +20 -9
- package/lib/hardware-sim/Labware/labwareInternals/LabwareWellLabels.d.ts +2 -2
- package/lib/hardware-sim/Labware/labwareInternals/StaticLabware.d.ts +2 -2
- package/lib/hardware-sim/Labware/labwareInternals/StrokedWells.d.ts +2 -2
- package/lib/hardware-sim/Labware/labwareInternals/StyledWells.d.ts +2 -2
- package/lib/hardware-sim/Labware/labwareInternals/Well.d.ts +1 -2
- package/lib/hardware-sim/Labware/labwareInternals/WellLabels.d.ts +2 -2
- package/lib/hardware-sim/Module/index.d.ts +24 -2
- package/lib/hardware-sim/Pipette/PipetteRender.d.ts +2 -2
- package/lib/hardware-sim/RobotCoordinateSpace/RobotCoordinateSpace.d.ts +5 -3
- package/lib/hocs/withStyleProps.d.ts +26 -0
- package/lib/hooks/useConditionalConfirm.d.ts +4 -0
- package/lib/icons/Icon.d.ts +7 -7
- package/lib/icons/ModuleIcon.d.ts +4 -3
- package/lib/instrument/index.d.ts +0 -2
- package/lib/legacy-hardware-sim/index.d.ts +0 -1
- package/lib/lists/index.d.ts +0 -1
- package/lib/molecules/CustomizeExpandButton/__tests__/CustomizeExpandButton.test.d.ts +1 -0
- package/lib/molecules/CustomizeExpandButton/index.d.ts +29 -0
- package/lib/molecules/DeckInfoLabel/index.d.ts +8 -16
- package/lib/molecules/index.d.ts +2 -1
- package/lib/organisms/CommandText/index.d.ts +2 -2
- package/lib/organisms/CommandText/useCommandTextString/index.d.ts +2 -2
- package/lib/organisms/CommandText/useCommandTextString/utils/getLabwareDefinitionsFromCommands.d.ts +2 -2
- package/lib/organisms/CommandText/useCommandTextString/utils/getLabwareLocation.d.ts +3 -3
- package/lib/organisms/CommandText/useCommandTextString/utils/getLabwareName.d.ts +2 -2
- package/lib/organisms/FixtureOption/index.d.ts +2 -2
- package/lib/organisms/ProtocolDeck/index.d.ts +10 -0
- package/lib/organisms/ProtocolTimelineScrubber/CommandItem.d.ts +2 -2
- package/lib/organisms/ProtocolTimelineScrubber/utils.d.ts +2 -2
- package/lib/organisms/index.d.ts +1 -0
- package/lib/primitives/ForeignObject.d.ts +8 -18
- package/lib/primitives/style-props.d.ts +1 -1
- package/lib/structure/index.d.ts +2 -3
- package/package.json +6 -3
- package/src/alerts/alerts.module.css +2 -2
- package/src/assets/localization/en/deck_configuration.json +12 -0
- package/src/assets/localization/en/index.ts +2 -0
- package/src/assets/localization/en/protocol_command_text.json +1 -1
- package/src/atoms/AnimationVideo/__tests__/AnimationVideo.test.tsx +40 -0
- package/src/atoms/AnimationVideo/index.tsx +44 -0
- package/src/atoms/CheckboxField/__tests__/CheckboxField.test.tsx +0 -3
- package/src/atoms/CheckboxField/index.tsx +1 -1
- package/src/atoms/InlineNotification/index.tsx +86 -70
- package/src/atoms/ListButton/ListButton.stories.tsx +21 -7
- package/src/atoms/ListButton/ListButtonChildren/index.ts +0 -1
- package/src/atoms/ListItem/ListItemChildren/ListItemDescriptor.tsx +2 -2
- package/src/atoms/MenuList/OverflowBtn.tsx +39 -41
- package/src/atoms/Snackbar/Snackbar.stories.tsx +59 -23
- package/src/atoms/Snackbar/index.tsx +48 -38
- package/src/atoms/StyledText/PlaceholderStyledText.tsx +46 -0
- package/src/atoms/StyledText/index.ts +1 -0
- package/src/atoms/StyledText/placeholderstyledtext.module.css +25 -0
- package/src/atoms/buttons/BasicButton.tsx +100 -0
- package/src/atoms/buttons/SecondaryButton.tsx +5 -2
- package/src/atoms/buttons/__tests__/BasicButton.test.tsx +87 -0
- package/src/atoms/buttons/buttons.stories.tsx +91 -55
- package/src/atoms/buttons/index.ts +1 -0
- package/src/atoms/index.ts +1 -0
- package/src/buttons/buttons.module.css +92 -92
- package/src/controls/styles.module.css +15 -15
- package/src/forms/Select.module.css +23 -23
- package/src/forms/forms.module.css +38 -37
- package/src/hardware-sim/BaseDeck/BaseDeck.stories.tsx +7 -7
- package/src/hardware-sim/BaseDeck/BaseDeck.tsx +98 -61
- package/src/hardware-sim/BaseDeck/WasteChuteFixture.tsx +22 -28
- package/src/hardware-sim/BaseDeck/basedeck.module.css +20 -0
- package/src/hardware-sim/Deck/FlexTrash.tsx +50 -30
- package/src/hardware-sim/Deck/MoveLabwareOnDeck.stories.tsx +2 -2
- package/src/hardware-sim/Deck/MoveLabwareOnDeck.tsx +122 -156
- package/src/hardware-sim/Deck/RobotCoordsForeignDiv.tsx +35 -27
- package/src/hardware-sim/Deck/RobotCoordsForeignObject.tsx +32 -29
- package/src/hardware-sim/Deck/RobotCoordsText.tsx +12 -11
- package/src/hardware-sim/Deck/RobotWorkSpace.tsx +74 -54
- package/src/hardware-sim/Deck/SlotLabels.tsx +51 -51
- package/src/hardware-sim/Deck/__tests__/resolveLabwareLocation.test.ts +138 -0
- package/src/hardware-sim/Deck/deck.module.css +54 -0
- package/src/hardware-sim/Deck/resolveLabwareLocation.ts +154 -0
- package/src/hardware-sim/DeckConfigurator/{FlexStackerFixture.tsx → AbsorbanceReaderItem.tsx} +34 -31
- package/src/hardware-sim/DeckConfigurator/{EmptyConfigFixture.tsx → EmptyConfigItem.tsx} +72 -44
- package/src/hardware-sim/DeckConfigurator/{AbsorbanceReaderFixture.tsx → FlexStackerItem.tsx} +46 -27
- package/src/hardware-sim/DeckConfigurator/{HeaterShakerFixture.tsx → HeaterShakerItem.tsx} +40 -28
- package/src/hardware-sim/DeckConfigurator/{MagneticBlockFixture.tsx → MagneticBlockItem.tsx} +41 -58
- package/src/hardware-sim/DeckConfigurator/{StagingAreaConfigFixture.tsx → StagingAreaConfigItem.tsx} +41 -23
- package/src/hardware-sim/DeckConfigurator/{StaticFixture.tsx → StaticItem.tsx} +16 -9
- package/src/hardware-sim/DeckConfigurator/{TemperatureModuleFixture.tsx → TemperatureModuleItem.tsx} +42 -26
- package/src/hardware-sim/DeckConfigurator/{ThermocyclerFixture.tsx → ThermocyclerItem.tsx} +31 -21
- package/src/hardware-sim/DeckConfigurator/{TrashBinConfigFixture.tsx → TrashBinConfigItem.tsx} +42 -27
- package/src/hardware-sim/DeckConfigurator/{WasteChuteConfigFixture.tsx → WasteChuteConfigItem.tsx} +38 -27
- package/src/hardware-sim/DeckConfigurator/constants.ts +5 -9
- package/src/hardware-sim/DeckConfigurator/index.tsx +236 -206
- package/src/hardware-sim/Labware/CalibrationBlockRender.stories.tsx +49 -0
- package/src/hardware-sim/Labware/CalibrationBlockRender.tsx +172 -0
- package/src/hardware-sim/Labware/Labware.tsx +30 -15
- package/src/hardware-sim/Labware/LabwareAdapter/index.tsx +2 -2
- package/src/hardware-sim/Labware/LabwareRender.stories.tsx +36 -21
- package/src/hardware-sim/Labware/LabwareRender.tsx +70 -15
- package/src/hardware-sim/Labware/__tests__/Labware.test.tsx +9 -4
- package/src/hardware-sim/Labware/__tests__/LabwareRender.test.tsx +14 -5
- package/src/hardware-sim/Labware/index.ts +1 -0
- package/src/hardware-sim/Labware/labware.module.css +8 -0
- package/src/hardware-sim/Labware/labwareInternals/FilledWells.tsx +3 -4
- package/src/hardware-sim/Labware/labwareInternals/LabwareOutline.tsx +48 -23
- package/src/hardware-sim/Labware/labwareInternals/LabwareWellLabels.tsx +3 -3
- package/src/hardware-sim/Labware/labwareInternals/StaticLabware.tsx +2 -2
- package/src/hardware-sim/Labware/labwareInternals/StrokedWells.tsx +2 -2
- package/src/hardware-sim/Labware/labwareInternals/StyledWells.tsx +2 -2
- package/src/hardware-sim/Labware/labwareInternals/Well.tsx +1 -2
- package/src/hardware-sim/Labware/labwareInternals/WellLabels.tsx +10 -11
- package/src/hardware-sim/Labware/labwareInternals/__tests__/LabwareWellLabels.test.tsx +2 -2
- package/src/hardware-sim/Labware/labwareInternals/__tests__/StrokedWells.test.tsx +2 -2
- package/src/hardware-sim/Labware/labwareInternals/__tests__/WellLabels.test.tsx +2 -2
- package/src/hardware-sim/Module/Module.stories.tsx +45 -15
- package/src/hardware-sim/Module/Thermocycler/index.tsx +2 -2
- package/src/hardware-sim/Module/index.tsx +41 -18
- package/src/hardware-sim/Pipette/PipetteRender.stories.tsx +2 -2
- package/src/hardware-sim/Pipette/PipetteRender.tsx +5 -7
- package/src/hardware-sim/Pipette/__tests__/PipetteRender.test.tsx +2 -2
- package/src/hardware-sim/RobotCoordinateSpace/RobotCoordinateSpace.tsx +15 -14
- package/src/hardware-sim/RobotCoordinateSpace/robotcoordinatespace.module.css +3 -0
- package/src/hocs/withStyleProps.ts +53 -0
- package/src/hooks/useConditionalConfirm.ts +15 -5
- package/src/icons/Icon.tsx +38 -29
- package/src/icons/ModuleIcon.tsx +5 -3
- package/src/icons/icon-data.ts +26 -2
- package/src/icons/icon.module.css +10 -0
- package/src/index.module.css +1 -0
- package/src/instrument/InstrumentDiagram.tsx +0 -2
- package/src/instrument/PipetteSelect.module.css +3 -3
- package/src/instrument/index.ts +0 -2
- package/src/legacy-hardware-sim/LabwareNameOverlay.module.css +5 -5
- package/src/legacy-hardware-sim/ModuleItem.module.css +19 -19
- package/src/legacy-hardware-sim/index.ts +0 -1
- package/src/lists/index.ts +0 -1
- package/src/lists/lists.module.css +23 -23
- package/src/modals/modals.module.css +55 -55
- package/src/molecules/CustomizeExpandButton/__tests__/CustomizeExpandButton.test.tsx +88 -0
- package/src/molecules/CustomizeExpandButton/index.tsx +189 -0
- package/src/molecules/DeckInfoLabel/DeckInfoLabel.stories.tsx +10 -6
- package/src/molecules/DeckInfoLabel/__tests__/DeckInfoLabel.test.tsx +15 -11
- package/src/molecules/DeckInfoLabel/deckinfolabel.module.css +42 -0
- package/src/molecules/DeckInfoLabel/index.tsx +36 -62
- package/src/molecules/DropdownMenu/index.tsx +17 -13
- package/src/molecules/InfoScreen/index.tsx +6 -2
- package/src/molecules/ReleaseNotes/styles.module.css +15 -15
- package/src/molecules/index.ts +2 -1
- package/src/nav/SidePanel.module.css +6 -6
- package/src/organisms/CommandText/__tests__/CommandText.test.tsx +12 -12
- package/src/organisms/CommandText/index.tsx +2 -2
- package/src/organisms/CommandText/useCommandTextString/index.ts +2 -2
- package/src/organisms/CommandText/useCommandTextString/utils/__tests__/getFinalLabwareLocation.test.ts +4 -4
- package/src/organisms/CommandText/useCommandTextString/utils/commandText/__tests__/getPipettingCommandText.test.tsx +3 -3
- package/src/organisms/CommandText/useCommandTextString/utils/commandText/getFlexStackerCommandText.ts +2 -4
- package/src/organisms/CommandText/useCommandTextString/utils/commandText/getMoveToWellCommandText.ts +19 -3
- package/src/organisms/CommandText/useCommandTextString/utils/commandText/getPipettingCommandText.ts +19 -10
- package/src/organisms/CommandText/useCommandTextString/utils/getLabwareDefinitionsFromCommands.ts +9 -9
- package/src/organisms/CommandText/useCommandTextString/utils/getLabwareLocation.tsx +3 -3
- package/src/organisms/CommandText/useCommandTextString/utils/getLabwareName.ts +2 -2
- package/src/organisms/DeckLabelSet/index.tsx +1 -3
- package/src/organisms/FixtureOption/__tests__/FixtureOption.test.tsx +0 -2
- package/src/organisms/FixtureOption/index.tsx +42 -71
- package/src/organisms/ProtocolDeck/index.tsx +118 -0
- package/src/organisms/ProtocolTimelineScrubber/CommandItem.tsx +2 -2
- package/src/organisms/ProtocolTimelineScrubber/PipetteVisuals.tsx +1 -1
- package/src/organisms/ProtocolTimelineScrubber/utils.ts +3 -3
- package/src/organisms/index.ts +1 -0
- package/src/primitives/ForeignObject.tsx +20 -61
- package/src/primitives/style-props.ts +1 -0
- package/src/slotmap/styles.module.css +1 -1
- package/src/structure/Pill.module.css +1 -1
- package/src/structure/Splash.module.css +1 -1
- package/src/structure/index.ts +2 -4
- package/src/structure/structure.module.css +11 -11
- package/src/styles/colors.module.css +1 -0
- package/src/styles/global.css +179 -0
- package/src/styles/index.module.css +1 -1
- package/src/styles/typography.module.css +1 -0
- package/src/testing/utils/renderWithProviders.tsx +3 -6
- package/src/tooltips/tooltips.module.css +14 -14
- package/vite.config.mts +6 -3
- package/lib/atoms/ListButton/ListButtonChildren/ListButtonRadioButton.d.ts +0 -14
- package/lib/hardware-sim/DeckConfigurator/AbsorbanceReaderFixture.d.ts +0 -10
- package/lib/hardware-sim/DeckConfigurator/EmptyConfigFixture.d.ts +0 -8
- package/lib/hardware-sim/DeckConfigurator/FlexStackerFixture.d.ts +0 -11
- package/lib/hardware-sim/DeckConfigurator/HeaterShakerFixture.d.ts +0 -10
- package/lib/hardware-sim/DeckConfigurator/MagneticBlockFixture.d.ts +0 -11
- package/lib/hardware-sim/DeckConfigurator/StagingAreaConfigFixture.d.ts +0 -10
- package/lib/hardware-sim/DeckConfigurator/TemperatureModuleFixture.d.ts +0 -10
- package/lib/hardware-sim/DeckConfigurator/ThermocyclerFixture.d.ts +0 -10
- package/lib/hardware-sim/DeckConfigurator/TrashBinConfigFixture.d.ts +0 -10
- package/lib/hardware-sim/DeckConfigurator/WasteChuteConfigFixture.d.ts +0 -11
- package/lib/hardware-sim/utils.d.ts +0 -3
- package/lib/instrument/InstrumentGroup.d.ts +0 -11
- package/lib/instrument/InstrumentInfo.d.ts +0 -18
- package/lib/legacy-hardware-sim/ModuleItem.d.ts +0 -17
- package/lib/lists/TitledList.d.ts +0 -44
- package/lib/structure/Card.d.ts +0 -18
- package/src/atoms/ListButton/ListButtonChildren/ListButtonRadioButton.tsx +0 -109
- package/src/atoms/ListButton/__tests__/ListButtonRadioButton.test.tsx +0 -28
- package/src/hardware-sim/utils.ts +0 -18
- package/src/instrument/InstrumentDiagram.stories.tsx +0 -43
- package/src/instrument/InstrumentGroup.stories.tsx +0 -89
- package/src/instrument/InstrumentGroup.tsx +0 -35
- package/src/instrument/InstrumentInfo.tsx +0 -106
- package/src/instrument/__tests__/InstrumentInfo.test.tsx +0 -58
- package/src/instrument/instrument.module.css +0 -45
- package/src/legacy-hardware-sim/ModuleItem.tsx +0 -145
- package/src/lists/TitledList.tsx +0 -154
- package/src/structure/Card.stories.tsx +0 -29
- package/src/structure/Card.tsx +0 -65
- /package/lib/atoms/{ListButton/__tests__/ListButtonRadioButton.test.d.ts → AnimationVideo/__tests__/AnimationVideo.test.d.ts} +0 -0
- /package/lib/{instrument/__tests__/InstrumentInfo.test.d.ts → atoms/buttons/__tests__/BasicButton.test.d.ts} +0 -0
|
@@ -1,16 +1,23 @@
|
|
|
1
|
-
import { css } from 'styled-components'
|
|
2
|
-
|
|
3
1
|
import type { ComponentProps, ReactNode } from 'react'
|
|
4
2
|
|
|
5
3
|
export interface RobotCoordsTextProps extends ComponentProps<'text'> {
|
|
6
4
|
x: number
|
|
5
|
+
/** The y-coordinate of the text baseline, in the Opentrons robot orientation, so +y is towards the top of the screen. */
|
|
7
6
|
y: number
|
|
8
7
|
children?: ReactNode
|
|
9
8
|
canHighlight?: boolean
|
|
10
9
|
}
|
|
11
10
|
|
|
12
|
-
/**
|
|
13
|
-
|
|
11
|
+
/**
|
|
12
|
+
* SVG text positioned with Opentrons robot coordinates.
|
|
13
|
+
*
|
|
14
|
+
* This should be used inside a wrapper like RobotWorkSpace that sets up a transformation
|
|
15
|
+
* to orient the SVG with the Opentrons coordinate convention, with +y towards the
|
|
16
|
+
* top of the screen.
|
|
17
|
+
*
|
|
18
|
+
* This component surgically undoes the wrapper's transformation so the child text is
|
|
19
|
+
* displayed right-side-up.
|
|
20
|
+
*/
|
|
14
21
|
export function RobotCoordsText(props: RobotCoordsTextProps): JSX.Element {
|
|
15
22
|
const { x, y, children, canHighlight = true, ...additionalProps } = props
|
|
16
23
|
return (
|
|
@@ -19,13 +26,7 @@ export function RobotCoordsText(props: RobotCoordsTextProps): JSX.Element {
|
|
|
19
26
|
x={x}
|
|
20
27
|
y={-1 * y}
|
|
21
28
|
transform="scale(1, -1)"
|
|
22
|
-
|
|
23
|
-
!canHighlight
|
|
24
|
-
? css`
|
|
25
|
-
user-select: none;
|
|
26
|
-
`
|
|
27
|
-
: undefined
|
|
28
|
-
}
|
|
29
|
+
style={{ userSelect: !canHighlight ? 'none' : undefined }}
|
|
29
30
|
>
|
|
30
31
|
{children}
|
|
31
32
|
</text>
|
|
@@ -2,80 +2,70 @@ import { useRef } from 'react'
|
|
|
2
2
|
|
|
3
3
|
import { OT2_ROBOT_TYPE } from '@opentrons/shared-data'
|
|
4
4
|
|
|
5
|
-
import { Svg } from '../../primitives'
|
|
6
5
|
import { DeckFromLayers } from './DeckFromLayers'
|
|
7
6
|
|
|
8
|
-
import type { ReactNode } from 'react'
|
|
9
|
-
import type {
|
|
10
|
-
import type { StyleProps } from '../../primitives'
|
|
7
|
+
import type { CSSProperties, ReactNode } from 'react'
|
|
8
|
+
import type { AddressableArea, DeckDefinition } from '@opentrons/shared-data'
|
|
11
9
|
|
|
12
10
|
export interface RobotWorkSpaceRenderProps {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
) => { x: number; y: number }
|
|
11
|
+
// todo(mm, 2025-06-05): Is this API still worthwhile? The parent of RobotWorkSpace
|
|
12
|
+
// already has access to the full DeckDefinition. Maybe it should iterate over
|
|
13
|
+
// the DeckDefinition's AddressableAreas itself?
|
|
14
|
+
addressableAreasById: { [addressableAreaId: string]: AddressableArea }
|
|
18
15
|
}
|
|
19
16
|
|
|
20
|
-
|
|
21
|
-
deckDef?: DeckDefinition
|
|
22
|
-
viewBox?: string | null
|
|
17
|
+
type BaseProps = {
|
|
23
18
|
children?: (props: RobotWorkSpaceRenderProps) => ReactNode
|
|
24
19
|
deckLayerBlocklist?: string[]
|
|
25
|
-
|
|
20
|
+
/** optional boolean to show the OT-2 deck from deck defintion layers */
|
|
26
21
|
showDeckLayers?: boolean
|
|
27
22
|
id?: string
|
|
28
|
-
}
|
|
23
|
+
} & (
|
|
24
|
+
| // Require at least one of deckDef or viewBox,
|
|
25
|
+
{
|
|
26
|
+
deckDef: DeckDefinition
|
|
27
|
+
}
|
|
28
|
+
| {
|
|
29
|
+
/**
|
|
30
|
+
* The x/y area to show, in standard SVG `x y xDimension yDimension` syntax.
|
|
31
|
+
*
|
|
32
|
+
* Specify coordinates in the Opentrons orientation, not the standard SVG orientation.
|
|
33
|
+
* So (x, y) is the area's front-left, xDimension extends the viewed area to the
|
|
34
|
+
* right, and yDimension extends the viewed area to the back.
|
|
35
|
+
*/
|
|
36
|
+
viewBox: string
|
|
37
|
+
}
|
|
38
|
+
)
|
|
29
39
|
|
|
30
|
-
type
|
|
40
|
+
export type RobotWorkSpaceProps = BaseProps & CSSProperties
|
|
31
41
|
|
|
32
|
-
|
|
42
|
+
/**
|
|
43
|
+
* A wrapper for rendering the robot deck, labware, etc. from a top-down perspective.
|
|
44
|
+
*
|
|
45
|
+
* Child SVG components can render themselves in the Opentrons coordinate orientation
|
|
46
|
+
* (+x to the right of the robot, +y to the back of the robot) and this component will
|
|
47
|
+
* SVG-transform them so they're displayed the correct way. This is needed because
|
|
48
|
+
* SVG inverts y compared to Opentrons coordinates.
|
|
49
|
+
*/
|
|
50
|
+
export function RobotWorkSpace(props: RobotWorkSpaceProps): JSX.Element {
|
|
33
51
|
const {
|
|
34
52
|
children,
|
|
35
|
-
deckDef,
|
|
36
53
|
deckLayerBlocklist = [],
|
|
37
54
|
showDeckLayers = false,
|
|
38
|
-
viewBox,
|
|
39
55
|
id,
|
|
40
56
|
...styleProps
|
|
41
57
|
} = props
|
|
58
|
+
const activeViewBox = getViewBoxFromProps(props)
|
|
59
|
+
const addressableAreasById = getAddressableAreasByIdFromProps(props)
|
|
42
60
|
const wrapperRef = useRef<SVGSVGElement>(null)
|
|
43
61
|
|
|
44
|
-
// NOTE: getScreenCTM in Chrome a DOMMatrix type,
|
|
45
|
-
// in Firefox the same fn returns a deprecated SVGMatrix.
|
|
46
|
-
// Until Firefox fixes this and conforms to SVG2 draft,
|
|
47
|
-
// it will suffer from inverted y behavior (ignores css transform)
|
|
48
|
-
const getRobotCoordsFromDOMCoords: GetRobotCoordsFromDOMCoords = (x, y) => {
|
|
49
|
-
if (!wrapperRef.current) return { x: 0, y: 0 }
|
|
50
|
-
|
|
51
|
-
const cursorPoint = wrapperRef.current.createSVGPoint()
|
|
52
|
-
|
|
53
|
-
cursorPoint.x = x
|
|
54
|
-
cursorPoint.y = y
|
|
55
|
-
|
|
56
|
-
return cursorPoint.matrixTransform(
|
|
57
|
-
wrapperRef.current.getScreenCTM()?.inverse()
|
|
58
|
-
)
|
|
59
|
-
}
|
|
60
|
-
if (!deckDef && !viewBox) return null
|
|
61
|
-
|
|
62
|
-
let wholeDeckViewBox
|
|
63
|
-
let deckSlotsById = {}
|
|
64
|
-
if (deckDef != null) {
|
|
65
|
-
const [viewBoxOriginX, viewBoxOriginY] = deckDef.cornerOffsetFromOrigin
|
|
66
|
-
const [deckXDimension, deckYDimension] = deckDef.dimensions
|
|
67
|
-
|
|
68
|
-
deckSlotsById = deckDef.locations.addressableAreas.reduce(
|
|
69
|
-
(acc, deckSlot) => ({ ...acc, [deckSlot.id]: deckSlot }),
|
|
70
|
-
{}
|
|
71
|
-
)
|
|
72
|
-
wholeDeckViewBox = `${viewBoxOriginX} ${viewBoxOriginY} ${deckXDimension} ${deckYDimension}`
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
const activeViewBox = viewBox ?? wholeDeckViewBox
|
|
76
|
-
|
|
77
62
|
return (
|
|
78
|
-
<
|
|
63
|
+
<svg
|
|
64
|
+
viewBox={activeViewBox}
|
|
65
|
+
ref={wrapperRef}
|
|
66
|
+
id={id}
|
|
67
|
+
style={{ ...styleProps }}
|
|
68
|
+
>
|
|
79
69
|
<g
|
|
80
70
|
transform={
|
|
81
71
|
activeViewBox
|
|
@@ -93,8 +83,38 @@ export function RobotWorkSpace(props: RobotWorkSpaceProps): JSX.Element | null {
|
|
|
93
83
|
robotType={OT2_ROBOT_TYPE}
|
|
94
84
|
/>
|
|
95
85
|
) : null}
|
|
96
|
-
{children?.({
|
|
86
|
+
{children?.({ addressableAreasById })}
|
|
97
87
|
</g>
|
|
98
|
-
</
|
|
88
|
+
</svg>
|
|
99
89
|
)
|
|
100
90
|
}
|
|
91
|
+
|
|
92
|
+
function getAddressableAreasByIdFromProps(
|
|
93
|
+
props: RobotWorkSpaceProps
|
|
94
|
+
): RobotWorkSpaceRenderProps['addressableAreasById'] {
|
|
95
|
+
if ('deckDef' in props) {
|
|
96
|
+
return Object.fromEntries(
|
|
97
|
+
props.deckDef.locations.addressableAreas.map(addressableArea => [
|
|
98
|
+
addressableArea.id,
|
|
99
|
+
addressableArea,
|
|
100
|
+
])
|
|
101
|
+
)
|
|
102
|
+
} else {
|
|
103
|
+
return {}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function getViewBoxFromProps(props: RobotWorkSpaceProps): string {
|
|
108
|
+
if ('viewBox' in props) {
|
|
109
|
+
// An explicitly provided viewBox takes precedence.
|
|
110
|
+
return props.viewBox
|
|
111
|
+
} else {
|
|
112
|
+
return getWholeDeckViewBox(props.deckDef)
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function getWholeDeckViewBox(deckDef: DeckDefinition): string {
|
|
117
|
+
const [viewBoxOriginX, viewBoxOriginY] = deckDef.cornerOffsetFromOrigin
|
|
118
|
+
const [deckXDimension, deckYDimension] = deckDef.dimensions
|
|
119
|
+
return `${viewBoxOriginX} ${viewBoxOriginY} ${deckXDimension} ${deckYDimension}`
|
|
120
|
+
}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
+
import clsx from 'clsx'
|
|
2
|
+
|
|
1
3
|
import { FLEX_ROBOT_TYPE } from '@opentrons/shared-data'
|
|
2
4
|
|
|
3
5
|
import { DeckInfoLabel } from '../../molecules'
|
|
4
|
-
import
|
|
5
|
-
import { ALIGN_CENTER, DIRECTION_COLUMN, JUSTIFY_CENTER } from '../../styles'
|
|
6
|
+
import styles from './deck.module.css'
|
|
6
7
|
import { RobotCoordsForeignObject } from './RobotCoordsForeignObject'
|
|
7
8
|
|
|
8
9
|
import type { RobotType } from '@opentrons/shared-data'
|
|
@@ -25,6 +26,26 @@ export const SlotLabels = ({
|
|
|
25
26
|
const widthSmallRem = 10.5
|
|
26
27
|
const widthLargeRem = 15.25
|
|
27
28
|
|
|
29
|
+
const rowDynamicWidth = show4thColumn
|
|
30
|
+
? `${widthSmallRem * 2 + widthLargeRem * 2}rem`
|
|
31
|
+
: `${widthSmallRem + widthLargeRem * 2}rem`
|
|
32
|
+
|
|
33
|
+
const itemDynamicWidth = show4thColumn
|
|
34
|
+
? `${widthSmallRem}rem`
|
|
35
|
+
: `${widthLargeRem}rem`
|
|
36
|
+
|
|
37
|
+
const rowStyle: Record<string, string> = {
|
|
38
|
+
'--dynamic-width': rowDynamicWidth,
|
|
39
|
+
}
|
|
40
|
+
const simpleItemWidthStyle: Record<string, string> = {
|
|
41
|
+
'--dynamic-width': `${widthLargeRem}rem`,
|
|
42
|
+
}
|
|
43
|
+
const nextTo4thWidthStyle: Record<string, string> = {
|
|
44
|
+
'--dynamic-width': itemDynamicWidth,
|
|
45
|
+
}
|
|
46
|
+
const fourthWidthStyle: Record<string, string> = {
|
|
47
|
+
'--dynamic-width': `${widthSmallRem}rem`,
|
|
48
|
+
}
|
|
28
49
|
return robotType === FLEX_ROBOT_TYPE ? (
|
|
29
50
|
<>
|
|
30
51
|
<RobotCoordsForeignObject
|
|
@@ -33,26 +54,20 @@ export const SlotLabels = ({
|
|
|
33
54
|
x="-147"
|
|
34
55
|
y="-10"
|
|
35
56
|
>
|
|
36
|
-
<
|
|
37
|
-
|
|
38
|
-
flexDirection={DIRECTION_COLUMN}
|
|
39
|
-
flex="1"
|
|
40
|
-
height="100%"
|
|
41
|
-
width="2.5rem"
|
|
42
|
-
>
|
|
43
|
-
<Flex alignItems={ALIGN_CENTER} flex="1">
|
|
57
|
+
<div className={styles.deck_labels_column}>
|
|
58
|
+
<div className={styles.deck_label_column_container}>
|
|
44
59
|
<DeckInfoLabel deckLabel="A" height="max-content" width="100%" />
|
|
45
|
-
</
|
|
46
|
-
<
|
|
60
|
+
</div>
|
|
61
|
+
<div className={styles.deck_label_column_container}>
|
|
47
62
|
<DeckInfoLabel deckLabel="B" height="max-content" width="100%" />
|
|
48
|
-
</
|
|
49
|
-
<
|
|
63
|
+
</div>
|
|
64
|
+
<div className={styles.deck_label_column_container}>
|
|
50
65
|
<DeckInfoLabel deckLabel="C" height="max-content" width="100%" />
|
|
51
|
-
</
|
|
52
|
-
<
|
|
66
|
+
</div>
|
|
67
|
+
<div className={styles.deck_label_column_container}>
|
|
53
68
|
<DeckInfoLabel deckLabel="D" height="max-content" width="100%" />
|
|
54
|
-
</
|
|
55
|
-
</
|
|
69
|
+
</div>
|
|
70
|
+
</div>
|
|
56
71
|
</RobotCoordsForeignObject>
|
|
57
72
|
<RobotCoordsForeignObject
|
|
58
73
|
height="2.5rem"
|
|
@@ -64,49 +79,34 @@ export const SlotLabels = ({
|
|
|
64
79
|
x="-100"
|
|
65
80
|
y="-55"
|
|
66
81
|
>
|
|
67
|
-
<
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
show4thColumn
|
|
72
|
-
? widthSmallRem * 2 + widthLargeRem * 2
|
|
73
|
-
: widthSmallRem + widthLargeRem * 2
|
|
74
|
-
}rem`}
|
|
75
|
-
height="2.5rem"
|
|
76
|
-
>
|
|
77
|
-
<Flex
|
|
78
|
-
alignItems={ALIGN_CENTER}
|
|
79
|
-
justifyContent={JUSTIFY_CENTER}
|
|
80
|
-
width={`${widthLargeRem}rem`}
|
|
82
|
+
<div className={clsx(styles.deck_labels_row)} style={rowStyle}>
|
|
83
|
+
<div
|
|
84
|
+
className={styles.deck_label_row_container}
|
|
85
|
+
style={simpleItemWidthStyle}
|
|
81
86
|
>
|
|
82
87
|
<DeckInfoLabel deckLabel="1" height="100%" />
|
|
83
|
-
</
|
|
84
|
-
<
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
width={`${widthSmallRem}rem`}
|
|
88
|
+
</div>
|
|
89
|
+
<div
|
|
90
|
+
className={styles.deck_label_row_container}
|
|
91
|
+
style={simpleItemWidthStyle}
|
|
88
92
|
>
|
|
89
93
|
<DeckInfoLabel deckLabel="2" height="100%" />
|
|
90
|
-
</
|
|
91
|
-
<
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
width={
|
|
95
|
-
show4thColumn ? `${widthSmallRem}rem` : `${widthLargeRem}rem`
|
|
96
|
-
}
|
|
94
|
+
</div>
|
|
95
|
+
<div
|
|
96
|
+
className={styles.deck_label_row_container}
|
|
97
|
+
style={nextTo4thWidthStyle}
|
|
97
98
|
>
|
|
98
99
|
<DeckInfoLabel deckLabel="3" height="100%" />
|
|
99
|
-
</
|
|
100
|
+
</div>
|
|
100
101
|
{show4thColumn ? (
|
|
101
|
-
<
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
width={`${widthSmallRem}rem`}
|
|
102
|
+
<div
|
|
103
|
+
className={styles.deck_label_row_container}
|
|
104
|
+
style={fourthWidthStyle}
|
|
105
105
|
>
|
|
106
106
|
<DeckInfoLabel deckLabel="4" height="100%" />
|
|
107
|
-
</
|
|
107
|
+
</div>
|
|
108
108
|
) : null}
|
|
109
|
-
</
|
|
109
|
+
</div>
|
|
110
110
|
</RobotCoordsForeignObject>
|
|
111
111
|
</>
|
|
112
112
|
) : null
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import { expect, it } from 'vitest'
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
getAllDefinitions,
|
|
5
|
+
getDeckDefinitions,
|
|
6
|
+
getModuleDef,
|
|
7
|
+
} from '@opentrons/shared-data'
|
|
8
|
+
|
|
9
|
+
import { resolveLabwareLocation } from '../resolveLabwareLocation'
|
|
10
|
+
|
|
11
|
+
import type { ModuleModel } from '@opentrons/shared-data'
|
|
12
|
+
|
|
13
|
+
it('should resolve a labware location', () => {
|
|
14
|
+
const labwareAUri = 'opentrons/nest_12_reservoir_15ml/1'
|
|
15
|
+
const labwareBUri = 'opentrons/agilent_1_reservoir_290ml/1'
|
|
16
|
+
const labwareCUri = 'opentrons/opentrons_96_pcr_adapter/1'
|
|
17
|
+
const labwareADefinition = getAllDefinitions()[labwareAUri]
|
|
18
|
+
const labwareBDefinition = getAllDefinitions()[labwareBUri]
|
|
19
|
+
const labwareCDefinition = getAllDefinitions()[labwareCUri]
|
|
20
|
+
|
|
21
|
+
const moduleModel: ModuleModel = 'temperatureModuleV2'
|
|
22
|
+
const moduleDefinition = getModuleDef(moduleModel)
|
|
23
|
+
|
|
24
|
+
const deckDefinition = getDeckDefinitions().ot3_standard
|
|
25
|
+
|
|
26
|
+
const slotId = 'C2'
|
|
27
|
+
|
|
28
|
+
const result = resolveLabwareLocation({
|
|
29
|
+
targetLabwareDef: labwareADefinition,
|
|
30
|
+
targetLabwareLocation: {
|
|
31
|
+
labwareId: 'labware-b-id',
|
|
32
|
+
},
|
|
33
|
+
otherLoadedLabware: [
|
|
34
|
+
{
|
|
35
|
+
id: 'labware-b-id',
|
|
36
|
+
definitionUri: labwareBUri,
|
|
37
|
+
location: {
|
|
38
|
+
labwareId: 'labware-c-id',
|
|
39
|
+
},
|
|
40
|
+
loadName: '',
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
id: 'labware-c-id',
|
|
44
|
+
definitionUri: labwareCUri,
|
|
45
|
+
location: {
|
|
46
|
+
moduleId: 'module-id',
|
|
47
|
+
},
|
|
48
|
+
loadName: '',
|
|
49
|
+
},
|
|
50
|
+
],
|
|
51
|
+
deckDef: deckDefinition,
|
|
52
|
+
otherLabwareDefinitions: [labwareBDefinition, labwareCDefinition],
|
|
53
|
+
loadedModules: [
|
|
54
|
+
{
|
|
55
|
+
id: 'module-id',
|
|
56
|
+
model: moduleModel,
|
|
57
|
+
location: {
|
|
58
|
+
slotName: slotId,
|
|
59
|
+
},
|
|
60
|
+
serialNumber: '',
|
|
61
|
+
},
|
|
62
|
+
],
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
const expectedResult: typeof result = {
|
|
66
|
+
deckDefinition,
|
|
67
|
+
slotId,
|
|
68
|
+
moduleDefinition,
|
|
69
|
+
labwareDefinitionsBottomToTop: [
|
|
70
|
+
labwareCDefinition,
|
|
71
|
+
labwareBDefinition,
|
|
72
|
+
labwareADefinition,
|
|
73
|
+
],
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
expect(result).toStrictEqual(expectedResult)
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
it('should return offDeck if the labware is off-deck', () => {
|
|
80
|
+
const labwareAUri = 'opentrons/nest_12_reservoir_15ml/1'
|
|
81
|
+
const labwareBUri = 'opentrons/agilent_1_reservoir_290ml/1'
|
|
82
|
+
const labwareADefinition = getAllDefinitions()[labwareAUri]
|
|
83
|
+
const labwareBDefinition = getAllDefinitions()[labwareBUri]
|
|
84
|
+
|
|
85
|
+
const deckDefinition = getDeckDefinitions().ot3_standard
|
|
86
|
+
|
|
87
|
+
const result = resolveLabwareLocation({
|
|
88
|
+
targetLabwareDef: labwareADefinition,
|
|
89
|
+
targetLabwareLocation: {
|
|
90
|
+
labwareId: 'labware-b-id',
|
|
91
|
+
},
|
|
92
|
+
otherLoadedLabware: [
|
|
93
|
+
{
|
|
94
|
+
id: 'labware-b-id',
|
|
95
|
+
definitionUri: labwareBUri,
|
|
96
|
+
location: 'offDeck',
|
|
97
|
+
loadName: '',
|
|
98
|
+
},
|
|
99
|
+
],
|
|
100
|
+
deckDef: deckDefinition,
|
|
101
|
+
otherLabwareDefinitions: [labwareBDefinition],
|
|
102
|
+
loadedModules: [],
|
|
103
|
+
})
|
|
104
|
+
|
|
105
|
+
const expectedResult: typeof result = 'offDeck'
|
|
106
|
+
|
|
107
|
+
expect(result).toStrictEqual(expectedResult)
|
|
108
|
+
})
|
|
109
|
+
|
|
110
|
+
it('should return error if something is missing from the input definitions', () => {
|
|
111
|
+
const labwareAUri = 'opentrons/nest_12_reservoir_15ml/1'
|
|
112
|
+
const labwareBUri = 'opentrons/agilent_1_reservoir_290ml/1'
|
|
113
|
+
const labwareADefinition = getAllDefinitions()[labwareAUri]
|
|
114
|
+
|
|
115
|
+
const deckDefinition = getDeckDefinitions().ot3_standard
|
|
116
|
+
|
|
117
|
+
const result = resolveLabwareLocation({
|
|
118
|
+
targetLabwareDef: labwareADefinition,
|
|
119
|
+
targetLabwareLocation: {
|
|
120
|
+
labwareId: 'labware-b-id',
|
|
121
|
+
},
|
|
122
|
+
otherLoadedLabware: [
|
|
123
|
+
{
|
|
124
|
+
id: 'labware-b-id',
|
|
125
|
+
definitionUri: labwareBUri,
|
|
126
|
+
location: 'offDeck',
|
|
127
|
+
loadName: '',
|
|
128
|
+
},
|
|
129
|
+
],
|
|
130
|
+
deckDef: deckDefinition,
|
|
131
|
+
otherLabwareDefinitions: [], // Missing labware B definition.
|
|
132
|
+
loadedModules: [],
|
|
133
|
+
})
|
|
134
|
+
|
|
135
|
+
const expectedResult: typeof result = 'error'
|
|
136
|
+
|
|
137
|
+
expect(result).toStrictEqual(expectedResult)
|
|
138
|
+
})
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
.deck_labels_column {
|
|
2
|
+
display: flex;
|
|
3
|
+
width: 2.5rem;
|
|
4
|
+
height: 100%;
|
|
5
|
+
flex: 1;
|
|
6
|
+
flex-direction: column;
|
|
7
|
+
align-items: center;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
.deck_label_column_container {
|
|
11
|
+
display: flex;
|
|
12
|
+
flex: 1;
|
|
13
|
+
align-items: center;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
.deck_labels_row {
|
|
17
|
+
display: flex;
|
|
18
|
+
width: var(--dynamic-width, 5rem); /* fallback if variable not set */
|
|
19
|
+
height: 2.5rem;
|
|
20
|
+
flex: 1;
|
|
21
|
+
align-items: center;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
.deck_label_row_container {
|
|
25
|
+
display: flex;
|
|
26
|
+
width: var(--dynamic-width);
|
|
27
|
+
align-items: center;
|
|
28
|
+
justify-content: center;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
.robot_coords_foreign_object_container {
|
|
32
|
+
display: flex;
|
|
33
|
+
width: 100%;
|
|
34
|
+
height: 100%;
|
|
35
|
+
transform: scale(1, -1);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
.trash_container {
|
|
39
|
+
display: flex;
|
|
40
|
+
width: 100%;
|
|
41
|
+
height: 100%;
|
|
42
|
+
align-items: center;
|
|
43
|
+
justify-content: center;
|
|
44
|
+
border-radius: var(--border-4);
|
|
45
|
+
gap: var(--spacing-8);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
.trash_container_highlight {
|
|
49
|
+
border: 3px solid var(--blue-50);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
.trash_container_rotate_copy {
|
|
53
|
+
transform-origin: center;
|
|
54
|
+
}
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import { getLabwareDefURI, getModuleDef } from '@opentrons/shared-data'
|
|
2
|
+
|
|
3
|
+
import type {
|
|
4
|
+
ComputeLabwareOriginInput,
|
|
5
|
+
DeckDefinition,
|
|
6
|
+
LabwareDefinition,
|
|
7
|
+
LabwareLocation,
|
|
8
|
+
LoadedLabware,
|
|
9
|
+
LoadedModule,
|
|
10
|
+
ModuleDefinition,
|
|
11
|
+
} from '@opentrons/shared-data'
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Converts a labware location to something that computeLabwareOrigin() can take as
|
|
15
|
+
* input and compute absolute deck coordinates from.
|
|
16
|
+
*
|
|
17
|
+
* This basically just does a lot of tedious lookups to resolve IDs into full definitions.
|
|
18
|
+
*
|
|
19
|
+
* Returns "error" if there was a problem doing the conversion, probably because of
|
|
20
|
+
* some invalid input to this function, like entries missing from `otherLoadedLabware`.
|
|
21
|
+
* Returns "offDeck" if the labware is known to be off-deck, so it doesn't have
|
|
22
|
+
* coordinates.
|
|
23
|
+
*/
|
|
24
|
+
export function resolveLabwareLocation({
|
|
25
|
+
deckDef,
|
|
26
|
+
targetLabwareDef,
|
|
27
|
+
targetLabwareLocation,
|
|
28
|
+
loadedModules,
|
|
29
|
+
otherLoadedLabware,
|
|
30
|
+
otherLabwareDefinitions,
|
|
31
|
+
}: {
|
|
32
|
+
deckDef: DeckDefinition
|
|
33
|
+
targetLabwareDef: LabwareDefinition
|
|
34
|
+
targetLabwareLocation: LabwareLocation
|
|
35
|
+
loadedModules: LoadedModule[]
|
|
36
|
+
otherLoadedLabware: LoadedLabware[]
|
|
37
|
+
otherLabwareDefinitions: LabwareDefinition[]
|
|
38
|
+
}): 'error' | 'offDeck' | ComputeLabwareOriginInput {
|
|
39
|
+
const labwareTopToBottom = resolveLabwareStack({
|
|
40
|
+
topLabwareDef: targetLabwareDef,
|
|
41
|
+
topLabwareLocation: targetLabwareLocation,
|
|
42
|
+
otherLoadedLabware,
|
|
43
|
+
otherLabwareDefinitions,
|
|
44
|
+
})
|
|
45
|
+
const labwareBottomToTop =
|
|
46
|
+
labwareTopToBottom !== 'error' ? labwareTopToBottom.toReversed() : 'error'
|
|
47
|
+
|
|
48
|
+
if (labwareBottomToTop === 'error' || labwareBottomToTop.length < 1) {
|
|
49
|
+
return 'error'
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const bottomLabwareLocation = labwareBottomToTop[0].location
|
|
53
|
+
|
|
54
|
+
if (
|
|
55
|
+
bottomLabwareLocation === 'offDeck' ||
|
|
56
|
+
bottomLabwareLocation === 'systemLocation'
|
|
57
|
+
) {
|
|
58
|
+
return 'offDeck'
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const bottom = (():
|
|
62
|
+
| 'error'
|
|
63
|
+
| {
|
|
64
|
+
slotId: string
|
|
65
|
+
moduleDefinition: ModuleDefinition | null
|
|
66
|
+
} => {
|
|
67
|
+
if ('moduleId' in bottomLabwareLocation) {
|
|
68
|
+
const loadedModule = loadedModules.find(
|
|
69
|
+
m => m.id === bottomLabwareLocation.moduleId
|
|
70
|
+
)
|
|
71
|
+
if (loadedModule == null) return 'error'
|
|
72
|
+
const moduleDefinition = getModuleDef(loadedModule.model)
|
|
73
|
+
const modSlot = deckDef.locations.addressableAreas.find(
|
|
74
|
+
s => s.id === loadedModule.location.slotName
|
|
75
|
+
)
|
|
76
|
+
if (modSlot == null) return 'error'
|
|
77
|
+
return { slotId: modSlot.id, moduleDefinition }
|
|
78
|
+
} else if ('slotName' in bottomLabwareLocation) {
|
|
79
|
+
return { slotId: bottomLabwareLocation.slotName, moduleDefinition: null }
|
|
80
|
+
} else if ('addressableAreaName' in bottomLabwareLocation) {
|
|
81
|
+
return {
|
|
82
|
+
slotId: bottomLabwareLocation.addressableAreaName,
|
|
83
|
+
moduleDefinition: null,
|
|
84
|
+
}
|
|
85
|
+
} else {
|
|
86
|
+
// Should not be reachable if resolveLabwareStack() did its job.
|
|
87
|
+
return 'error'
|
|
88
|
+
}
|
|
89
|
+
})()
|
|
90
|
+
|
|
91
|
+
if (bottom === 'error') {
|
|
92
|
+
return 'error'
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return {
|
|
96
|
+
deckDefinition: deckDef,
|
|
97
|
+
slotId: bottom.slotId,
|
|
98
|
+
moduleDefinition: bottom.moduleDefinition,
|
|
99
|
+
labwareDefinitionsBottomToTop: labwareBottomToTop.map(l => l.definition),
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Return the labware comprising the labware part of a stack. In other words, everything
|
|
105
|
+
* from the top labware down to, and excluding, the underlying module or deck slot.
|
|
106
|
+
*/
|
|
107
|
+
function resolveLabwareStack({
|
|
108
|
+
topLabwareDef,
|
|
109
|
+
topLabwareLocation,
|
|
110
|
+
otherLoadedLabware,
|
|
111
|
+
otherLabwareDefinitions,
|
|
112
|
+
}: {
|
|
113
|
+
topLabwareDef: LabwareDefinition
|
|
114
|
+
topLabwareLocation: LabwareLocation
|
|
115
|
+
otherLoadedLabware: LoadedLabware[]
|
|
116
|
+
otherLabwareDefinitions: LabwareDefinition[]
|
|
117
|
+
}):
|
|
118
|
+
| 'error'
|
|
119
|
+
| Array<{
|
|
120
|
+
definition: LabwareDefinition
|
|
121
|
+
location: LabwareLocation
|
|
122
|
+
}> {
|
|
123
|
+
let bottomMostLabwareSoFar = {
|
|
124
|
+
definition: topLabwareDef,
|
|
125
|
+
location: topLabwareLocation,
|
|
126
|
+
}
|
|
127
|
+
const labwareTopToBottomSoFar = [bottomMostLabwareSoFar]
|
|
128
|
+
|
|
129
|
+
while (
|
|
130
|
+
typeof bottomMostLabwareSoFar.location === 'object' &&
|
|
131
|
+
'labwareId' in bottomMostLabwareSoFar.location
|
|
132
|
+
) {
|
|
133
|
+
const newBottomLabwareId = bottomMostLabwareSoFar.location.labwareId
|
|
134
|
+
const newBottomLabware = otherLoadedLabware.find(
|
|
135
|
+
l => l.id === newBottomLabwareId
|
|
136
|
+
)
|
|
137
|
+
const newBottomLabwareDefinition = otherLabwareDefinitions.find(
|
|
138
|
+
def => getLabwareDefURI(def) === newBottomLabware?.definitionUri
|
|
139
|
+
)
|
|
140
|
+
if (newBottomLabware == null || newBottomLabwareDefinition == null) {
|
|
141
|
+
console.warn(
|
|
142
|
+
`Expected to find details for labware ID ${newBottomLabwareId} but could not.`
|
|
143
|
+
)
|
|
144
|
+
return 'error'
|
|
145
|
+
}
|
|
146
|
+
bottomMostLabwareSoFar = {
|
|
147
|
+
definition: newBottomLabwareDefinition,
|
|
148
|
+
location: newBottomLabware.location,
|
|
149
|
+
}
|
|
150
|
+
labwareTopToBottomSoFar.push(bottomMostLabwareSoFar)
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
return labwareTopToBottomSoFar
|
|
154
|
+
}
|