Skip to content

Commit 82f33e0

Browse files
authored
feat(abr-testing): Mix Protocol with Automated Error Recovery (#18910)
<!-- Thanks for taking the time to open a Pull Request (PR)! Please make sure you've read the "Opening Pull Requests" section of our Contributing Guide: https://github.com/Opentrons/opentrons/blob/edge/CONTRIBUTING.md#opening-pull-requests GitHub provides robust markdown to format your PR. Links, diagrams, pictures, and videos along with text formatting make it possible to create a rich and informative PR. For more information on GitHub markdown, see: https://docs.github.com/en/get-started/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax To ensure your code is reviewed quickly and thoroughly, please fill out the sections below to the best of your ability! --> # Overview Protocol that mixes and handles stalls ## Test Plan and Hands on Testing - Tested protocol with 8ch 1000 ul, 200 ul tips, and water - Initiated stall - Watched successful error recovery ## Changelog - Protocol added to mix - If stall occurs, pipette homes the z axis and attempts to blow_out() liquid in the tips - If a PositionUnknownError occurs, all of the axises are homed excluding the pipette plungers - If no other error occurs, the pipette continues mixing - If another error does occur, the error is raised. ## Review requests ## Risk assessment - Protocol will only work if error recovery is disabled on the robot - Haven't tested with different liquid classes
1 parent fa27d82 commit 82f33e0

File tree

1 file changed

+157
-0
lines changed

1 file changed

+157
-0
lines changed
Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
"""Mix Protocol with Error Recovery Turned Off."""
2+
from opentrons.protocol_api import (
3+
ProtocolContext,
4+
ParameterContext,
5+
Well,
6+
InstrumentContext,
7+
)
8+
from opentrons.hardware_control.types import Axis
9+
10+
metadata = {
11+
"protocolName": "Mix without Error Recovery",
12+
"author": "Rhyann Clarke <[email protected]>",
13+
}
14+
requirements = {
15+
"robotType": "Flex",
16+
"apiLevel": "2.24",
17+
}
18+
19+
20+
def add_parameters(parameters: ParameterContext) -> None:
21+
"""Add parameters for the Mix protocol."""
22+
parameters.add_str(
23+
variable_name="left_mount",
24+
display_name="Left Mount",
25+
description="Pipette Type on Left Mount.",
26+
choices=[
27+
{"display_name": "8ch 50ul", "value": "flex_8channel_50"},
28+
{"display_name": "8ch 1000ul", "value": "flex_8channel_1000"},
29+
{"display_name": "1ch 50ul", "value": "flex_1channel_50"},
30+
{"display_name": "1ch 1000ul", "value": "flex_1channel_1000"},
31+
{"display_name": "96ch 1000ul", "value": "flex_96channel_1000"},
32+
{"display_name": "None", "value": "none"},
33+
],
34+
default="flex_8channel_1000",
35+
)
36+
parameters.add_str(
37+
variable_name="tip_type",
38+
display_name="Tip Type",
39+
description="Tip Type to use for the test.",
40+
choices=[
41+
{"display_name": "50 uL", "value": "opentrons_flex_96_tiprack_50ul"},
42+
{
43+
"display_name": "50 uL Filter",
44+
"value": "opentrons_flex_96_filtertiprack_50ul",
45+
},
46+
{
47+
"display_name": "200 µL",
48+
"value": "opentrons_flex_96_tiprack_200ul",
49+
},
50+
{"display_name": "1000 µL", "value": "opentrons_flex_96_tiprack_1000ul"},
51+
{
52+
"display_name": "1000 µL FILTER",
53+
"value": "opentrons_flex_96_filtertiprack_1000ul",
54+
},
55+
],
56+
default="opentrons_flex_96_tiprack_200ul",
57+
)
58+
parameters.add_str(
59+
variable_name="tip_rack_slot",
60+
display_name="Tip Rack Slot",
61+
description="Slot for the tip rack.",
62+
choices=[
63+
{"display_name": "A1", "value": "A1"},
64+
{"display_name": "B1", "value": "B1"},
65+
{"display_name": "C1", "value": "C1"},
66+
{"display_name": "D1", "value": "D1"},
67+
{"display_name": "A3", "value": "A3"},
68+
{"display_name": "B3", "value": "B3"},
69+
{"display_name": "C3", "value": "C3"},
70+
{"display_name": "D3", "value": "D3"},
71+
],
72+
default="D1",
73+
)
74+
parameters.add_float(
75+
variable_name="mix_volume",
76+
display_name="Mix Volume (ul)",
77+
maximum=1000,
78+
minimum=1,
79+
default=200,
80+
)
81+
parameters.add_int(
82+
variable_name="mix_reps",
83+
display_name="Mix Repititions",
84+
maximum=10000,
85+
minimum=1,
86+
default=200,
87+
)
88+
parameters.add_float(
89+
variable_name="flow_rate",
90+
display_name="Flow Rate",
91+
description="Mix flow rate.",
92+
maximum=850,
93+
minimum=1,
94+
default=716,
95+
)
96+
97+
98+
def home_axes_only(ctx: ProtocolContext) -> None:
99+
"""Home axes and no plungers."""
100+
hw_api = ctx._core.get_hardware()
101+
hw_api.home([Axis.Z_L, Axis.Z_R, Axis.X, Axis.Y])
102+
103+
104+
def safe_mix(
105+
pipette: InstrumentContext,
106+
location: Well,
107+
ctx: ProtocolContext,
108+
) -> None:
109+
"""Perform mix manually, catching stalls."""
110+
stall_count = 0
111+
mix_volume = ctx.params.mix_volume # type: ignore[attr-defined]
112+
mix_reps = ctx.params.mix_reps # type: ignore[attr-defined]
113+
mix_flow_rate = ctx.params.flow_rate # type: ignore[attr-defined]
114+
115+
for i in range(mix_reps):
116+
try:
117+
pipette.aspirate(mix_volume, location.bottom(z=1), flow_rate=mix_flow_rate)
118+
pipette.dispense(mix_volume, location.bottom(z=2), flow_rate=mix_flow_rate)
119+
except Exception as e:
120+
msg = str(e)
121+
if "stall" in msg:
122+
ctx.comment(f"Caught stall error (code 2003): {msg}")
123+
try:
124+
pipette._retract() # homes the z axis
125+
except Exception as e:
126+
msg = str(e)
127+
ctx.comment(f"second error {msg}")
128+
if "position" in msg:
129+
# if PositionUnknownError Occurs after the Z axis homes, t
130+
# he pipette will home all axes excluding the plunger.
131+
home_axes_only(ctx)
132+
pipette.blow_out(location.top())
133+
pipette.home() # pipette homes again after blow out to ensure plunger is homed.
134+
pipette.move_to(location.top())
135+
else:
136+
ctx.comment(f"Unhandled error: {msg}")
137+
raise
138+
stall_count += 1
139+
continue
140+
ctx.comment(f"Total stalls: {stall_count}")
141+
142+
143+
def run(ctx: ProtocolContext) -> None:
144+
"""Run the Mix protocol."""
145+
# Load pipette
146+
left_mount = ctx.params.left_mount # type: ignore[attr-defined]
147+
tip_rack = ctx.load_labware(
148+
ctx.params.tip_type, ctx.params.tip_rack_slot # type: ignore[attr-defined]
149+
)
150+
pipette = ctx.load_instrument(
151+
left_mount,
152+
"left",
153+
tip_racks=[tip_rack],
154+
)
155+
reservoir = ctx.load_labware("opentrons_tough_12_reservoir_22ml", "D2")
156+
pipette.pick_up_tip()
157+
safe_mix(pipette, reservoir["A1"], ctx)

0 commit comments

Comments
 (0)