Skip to content

Commit 11f8a10

Browse files
authored
Merge branch 'edge' into stacker-step-summary
2 parents 1b6d834 + daadb45 commit 11f8a10

File tree

8 files changed

+467
-54
lines changed

8 files changed

+467
-54
lines changed

api/docs/v2/basic_commands/liquids.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ This example measures the liquid height in well A2 of a plate and then immediate
7575

7676
To ensure the pipette stays submerged while aspirating, set ``target="end"`` for the aspirate or use multiple location parameters. For more, see :ref:`well-meniscus`.
7777

78-
!!! note::
78+
.. note::
7979
``measure_liquid_height()`` works best with a new pipette tip each time. To save time and tips throughout your protocol, use ``Labware.load_liquid`` instead to specify starting liquid volumes.
8080

8181
Use the ``location`` and ``end_location`` parameters in combination to direct the pipette to move to specific locations while aspirating::
@@ -192,7 +192,7 @@ This example measures the liquid height in well B1 of a plate and then immediate
192192

193193
To ensure the pipette begins the dispense at the liquid meniscus, set ``target="start"``. See :ref:`well-meniscus` for more details on pipetting relative to the liquid meniscus.
194194

195-
!!! note::
195+
.. note::
196196
``measure_liquid_height()`` works best with a new pipette tip each time. To save time and tips throughout your protocol, use ``Labware.load_liquid`` instead to specify starting liquid volumes.
197197

198198

api/docs/v2/robot_position.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,8 @@ Here, the pipette tip stays at 1 mm below the liquid meniscus, regardless of cha
117117
Detecting liquid in a well requires pipette sensors, so you can only measure liquid height with a Flex pipette.
118118

119119
.. versionadded:: 2.23
120+
.. versionchanged:: 2.27
121+
Use the optional ``end_location`` parameter to pipette relative to the liquid meniscus as it changes.
120122

121123
.. _new-default-op-positions:
122124

Lines changed: 211 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,211 @@
1+
import {
2+
COLORS,
3+
LabwareRender,
4+
MODULE_ICON_NAME_BY_TYPE,
5+
RobotInfoLabel,
6+
RobotWorkSpace,
7+
StyledText,
8+
} from '@opentrons/components'
9+
import { getLabwareViewBox } from '@opentrons/shared-data'
10+
import {
11+
getSlotInLocationStack,
12+
wellFillFromWellContents,
13+
} from '@opentrons/step-generation'
14+
15+
import { getAllWellContentsAtFrame } from '../../utils/getAllWellContentsAtFrame'
16+
import { WellContainer } from '../../WellContainer'
17+
import { WellTooltip } from '../../WellTooltip'
18+
import styles from './labwareslot.module.css'
19+
20+
import type { WellGroup } from '@opentrons/components'
21+
import type { Liquid, RunTimeCommand } from '@opentrons/shared-data'
22+
import type {
23+
LabwareEntities,
24+
ModuleEntities,
25+
PipetteEntities,
26+
RobotState,
27+
} from '@opentrons/step-generation'
28+
29+
interface LabwareSlotContainerProps {
30+
topLabwareOnSlotId: string
31+
labwareEntities: LabwareEntities
32+
commands: RunTimeCommand[]
33+
liquids: Liquid[]
34+
currentCommand: RunTimeCommand
35+
robotState: RobotState
36+
pipetteEntities: PipetteEntities
37+
moduleEntities: ModuleEntities
38+
}
39+
export function LabwareSlot(props: LabwareSlotContainerProps): JSX.Element {
40+
const {
41+
commands,
42+
liquids,
43+
topLabwareOnSlotId,
44+
labwareEntities,
45+
currentCommand,
46+
robotState,
47+
pipetteEntities,
48+
moduleEntities,
49+
} = props
50+
const { labware, pipettes, liquidState } = robotState
51+
const labwareLoadCommand = Object.values(commands).find(
52+
command =>
53+
'labwareId' in command.result &&
54+
command.result.labwareId === topLabwareOnSlotId
55+
)
56+
const pipetteTemporalProperties = Object.entries(pipettes).find(
57+
([_, pipette]) => pipette.entityId === topLabwareOnSlotId
58+
)
59+
const slot = getSlotInLocationStack(labware[topLabwareOnSlotId].stack)
60+
61+
const { params: labwareLoadCommandParams } = labwareLoadCommand ?? {}
62+
const labwareNickname: string | null =
63+
labwareLoadCommandParams != null &&
64+
'displayName' in labwareLoadCommandParams
65+
? labwareLoadCommandParams.displayName
66+
: null
67+
const { params } = currentCommand
68+
const labwareDef = labwareEntities[topLabwareOnSlotId].def
69+
const labwareDisplayName = labwareDef.metadata.displayName
70+
71+
const liquidDisplayColors = Object.fromEntries(
72+
liquids.map(({ id, displayColor }) => [id, displayColor ?? COLORS.grey40])
73+
)
74+
const allWellContentsForActiveItem = getAllWellContentsAtFrame(
75+
liquidState,
76+
labwareDef
77+
)
78+
const wellContents =
79+
allWellContentsForActiveItem != null
80+
? allWellContentsForActiveItem[topLabwareOnSlotId]
81+
: null
82+
83+
const wellFill = wellFillFromWellContents(wellContents, liquidDisplayColors)
84+
const activeWellName =
85+
pipetteTemporalProperties != null
86+
? pipetteTemporalProperties[1].wellName
87+
: null
88+
const wellGroup: WellGroup | null =
89+
activeWellName != null
90+
? {
91+
[activeWellName]: null,
92+
}
93+
: null
94+
const { wells } = labwareDef
95+
96+
const pipetteLocationLiquidState =
97+
pipetteTemporalProperties != null
98+
? liquidState.pipettes[pipetteTemporalProperties[0]]?.[0]
99+
: null
100+
const labwareLocationLiquidState =
101+
activeWellName != null
102+
? liquidState.labware[topLabwareOnSlotId]?.[activeWellName]
103+
: null
104+
105+
const tipMaxVolume =
106+
pipetteTemporalProperties != null
107+
? pipetteEntities[pipetteTemporalProperties[0]].spec.liquids.default
108+
.maxVolume
109+
: 0
110+
111+
const labwareViewBox = getLabwareViewBox(labwareDef)
112+
const ingredNames = liquids.reduce(
113+
(acc: Record<string, string>, { id, displayName }) => {
114+
acc[id] = displayName
115+
return acc
116+
},
117+
{}
118+
)
119+
120+
return (
121+
<>
122+
{activeWellName != null ? (
123+
<WellContainer
124+
wells={wells}
125+
params={params}
126+
activeWellName={activeWellName}
127+
wellColor={wellFill[activeWellName]}
128+
labwareLocationLiquidState={labwareLocationLiquidState}
129+
pipetteLocationLiquidState={pipetteLocationLiquidState}
130+
liquids={liquids}
131+
tipMaxVolume={tipMaxVolume}
132+
/>
133+
) : null}
134+
<div className={styles.container}>
135+
<div className={styles.header}>
136+
{/* header icon part */}
137+
<div>
138+
{labware[topLabwareOnSlotId]?.stack
139+
.filter(item => item !== topLabwareOnSlotId)
140+
.reverse()
141+
.map(item => {
142+
if (moduleEntities[item] != null) {
143+
return (
144+
<RobotInfoLabel
145+
key={item}
146+
iconName={
147+
MODULE_ICON_NAME_BY_TYPE[moduleEntities[item].type]
148+
}
149+
/>
150+
)
151+
} else if (labware[item] != null) {
152+
return <RobotInfoLabel key={item} iconName="stacked" />
153+
} else {
154+
return <RobotInfoLabel key={item} deckLabel={slot} />
155+
}
156+
})}
157+
</div>
158+
{/* header icon part */}
159+
160+
{/* header text part */}
161+
<div className={styles.header_text}>
162+
{labwareNickname != null ? (
163+
<StyledText desktopStyle="captionSemiBold">
164+
{labwareNickname}
165+
</StyledText>
166+
) : null}
167+
<StyledText desktopStyle="bodyDefaultRegular">
168+
{labwareDisplayName}
169+
</StyledText>
170+
</div>
171+
{/* header text part */}
172+
</div>
173+
<div className={styles.body_container}>
174+
<WellTooltip ingredNames={ingredNames}>
175+
{({ makeHandleMouseEnterWell, handleMouseLeaveWell }) => (
176+
<div className={styles.labware_render_container}>
177+
<RobotWorkSpace
178+
key={topLabwareOnSlotId}
179+
viewBox={`${labwareViewBox.minX} ${labwareViewBox.minY} ${labwareViewBox.xDimension} ${labwareViewBox.yDimension}`}
180+
>
181+
{() => (
182+
<g>
183+
<LabwareRender
184+
definition={labwareDef}
185+
positioningMode="passThrough"
186+
wellFill={wellFill}
187+
highlightedWells={wellGroup}
188+
onMouseLeaveWell={mouseEventArgs => {
189+
handleMouseLeaveWell(mouseEventArgs)
190+
handleMouseLeaveWell(mouseEventArgs.event)
191+
}}
192+
onMouseEnterWell={({ wellName, event }) => {
193+
if (wellContents != null) {
194+
makeHandleMouseEnterWell(
195+
wellName,
196+
wellContents[wellName]?.ingreds
197+
)(event)
198+
}
199+
}}
200+
/>
201+
</g>
202+
)}
203+
</RobotWorkSpace>
204+
</div>
205+
)}
206+
</WellTooltip>
207+
</div>
208+
</div>
209+
</>
210+
)
211+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
.container {
2+
display: flex;
3+
flex-direction: column;
4+
padding: var(--spacing-16) var(--spacing-20) var(--spacing-20);
5+
gap: var(--spacing-16);
6+
}
7+
8+
.header {
9+
display: flex;
10+
flex-direction: column;
11+
gap: var(--spacing-4);
12+
}
13+
14+
.header_text {
15+
display: flex;
16+
flex-direction: column;
17+
}
18+
19+
.body_container {
20+
padding: var(--spacing-16) var(--spacing-24);
21+
border-radius: var(--border-radius-4);
22+
background-color: var(--grey-10);
23+
}

0 commit comments

Comments
 (0)