Skip to content

Commit b16b250

Browse files
rclarke0Carlos-fernandez
authored andcommitted
feat (hardware-testing): DVT Flex Stacker Labware Compatibility Lifetime Test (#17898)
<!-- 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 DVT Flex Stacker Labware Compatibility Lifetime Test ## Test Plan and Hands on Testing - Ran in SZ ## Changelog <!-- List changes introduced by this PR considering future developers and the end user. Give careful thought and clear documentation to breaking changes. --> ## Review requests <!-- - What do you need from reviewers to feel confident this PR is ready to merge? - Ask questions. --> ## Risk assessment <!-- - Indicate the level of attention this PR needs. - Provide context to guide reviewers. - Discuss trade-offs, coupling, and side effects. - Look for the possibility, even if you think it's small, that your change may affect some other part of the system. - For instance, changing return tip behavior may also change the behavior of labware calibration. - How do your unit tests and on hands on testing mitigate this PR's risks and the risk of future regressions? - Especially in high risk PRs, explain how you know your testing is enough. --> --------- Co-authored-by: Carlos <[email protected]>
1 parent 3fc060c commit b16b250

File tree

1 file changed

+368
-0
lines changed

1 file changed

+368
-0
lines changed
Lines changed: 368 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,368 @@
1+
"""DVT Flex Stacker with 4 or 2 stackers.
2+
3+
This protocol is used to validate stacker store/dispense commands
4+
5+
"""
6+
7+
from opentrons.protocol_api import ParameterContext, ProtocolContext
8+
from opentrons.protocol_api.module_contexts import (
9+
FlexStackerContext,
10+
)
11+
from datetime import datetime
12+
from typing import Optional, Dict, Any
13+
import os
14+
import csv
15+
16+
metadata = {
17+
"protocolName": "Flex Stacker Labware Compatibility Lifetime Test.",
18+
"author": "Opentrons <[email protected]>",
19+
"description": "This protocol dispenses labware from one stacker and into"
20+
"another stacker, the second stacker then stores it, and the actions are"
21+
"reversed.",
22+
}
23+
requirements = {
24+
"robotType": "Flex",
25+
"apiLevel": "2.23",
26+
}
27+
28+
test_data: Dict[str, Optional[Any]] = {
29+
"Cycles": None,
30+
"Stacker SN": None,
31+
"State": None,
32+
"labware": None,
33+
"plate_num": None,
34+
"Error": None,
35+
}
36+
37+
38+
def add_parameters(parameters: ParameterContext) -> None:
39+
"""Runtime parameters."""
40+
parameters.add_str(
41+
display_name="Target Labware",
42+
variable_name="labware_name",
43+
description="The labware that will be stored/dispensed.",
44+
default="nest_96_wellplate_2ml_deep",
45+
choices=[
46+
{
47+
"display_name": "Opentrons 96 Tipack 50ul",
48+
"value": "opentrons_flex_96_tiprack_50ul",
49+
},
50+
{
51+
"display_name": "Opentrons 96 Tipack 200ul",
52+
"value": "opentrons_flex_96_tiprack_200ul",
53+
},
54+
{
55+
"display_name": "Opentrons 96 Tipack 1000ul",
56+
"value": "opentrons_flex_96_tiprack_1000ul",
57+
},
58+
{
59+
"display_name": "NEST PCR Plates",
60+
"value": "nest_96_wellplate_100ul_pcr_full_skirt",
61+
},
62+
{
63+
"display_name": "NEST 96 Deep Well Plates",
64+
"value": "nest_96_wellplate_2ml_deep",
65+
},
66+
{
67+
"display_name": "NEST 96 Flat Bottom Plates",
68+
"value": "nest_96_wellplate_200ul_flat",
69+
},
70+
],
71+
)
72+
parameters.add_int(
73+
display_name="Cycles",
74+
variable_name="test_cycles",
75+
description="The number of cycles of dispensing/storing to perform.",
76+
default=5616,
77+
minimum=1,
78+
maximum=5616,
79+
)
80+
parameters.add_int(
81+
display_name="Labware Count",
82+
variable_name="labware_count",
83+
description="The number of labware in the stacker",
84+
default=6,
85+
minimum=1,
86+
maximum=100,
87+
)
88+
89+
parameters.add_str(
90+
display_name="Number of Stackers",
91+
variable_name="stackers_mounted",
92+
description="choose the stackers that is on the deck.",
93+
default="D4 C4 B4 A4",
94+
choices=[
95+
{"display_name": "4 STACKERS", "value": "D4 C4 B4 A4"},
96+
{"display_name": "2 STACKERS (D4, C4)", "value": "D4 C4"},
97+
],
98+
)
99+
100+
101+
def record_test_data(
102+
test_data: Dict[str, Optional[Any]],
103+
cycle: int,
104+
SNs: str,
105+
state: str,
106+
labware_name: str,
107+
plate_num: int,
108+
log_file: Any,
109+
csvfile: Any,
110+
) -> None:
111+
"""Records test data."""
112+
test_data["Cycles"] = cycle
113+
test_data["Stacker SN"] = SNs
114+
test_data["State"] = state
115+
test_data["labware"] = labware_name
116+
test_data["plate_num"] = plate_num
117+
test_data["Error"] = None
118+
log_file.writerow(test_data)
119+
csvfile.flush()
120+
121+
122+
def run(protocol: ProtocolContext) -> None:
123+
"""Protocol."""
124+
directory = f'/data/dvt_stacker_lifetime_{datetime.now().strftime("%m_%d_%y")}'
125+
if not os.path.exists(directory):
126+
os.makedirs(directory)
127+
labware_name = protocol.params.labware_name # type: ignore[attr-defined]
128+
labware_count = protocol.params.labware_count # type: ignore[attr-defined]
129+
test_cycles = protocol.params.test_cycles # type: ignore[attr-defined]
130+
stackers_mounted = protocol.params.stackers_mounted # type: ignore[attr-defined]
131+
deck_slots_for_stackers = stackers_mounted.split()
132+
133+
# define lid
134+
if "tiprack" in labware_name:
135+
tiprack_lid = "opentrons_flex_tiprack_lid"
136+
else:
137+
tiprack_lid = None
138+
139+
# ======================= Stacker Setup ======================
140+
s_num = 0
141+
SNs = []
142+
num_of_stackers = len(deck_slots_for_stackers)
143+
for d in deck_slots_for_stackers:
144+
s_num += 1
145+
globals()[f"f_stacker_{s_num}"]: FlexStackerContext = protocol.load_module( # type: ignore
146+
"flexStackerModuleV1", d
147+
)
148+
if s_num % 2 == 0:
149+
globals()[f"f_stacker_{s_num}"].set_stored_labware(
150+
load_name=labware_name,
151+
count=0, # always zero so we can store the labware
152+
lid=tiprack_lid
153+
if "opentrons_flex_96_tiprack" in labware_name
154+
else None,
155+
)
156+
else:
157+
globals()[f"f_stacker_{s_num}"].set_stored_labware(
158+
load_name=labware_name,
159+
count=labware_count,
160+
lid=tiprack_lid
161+
if "opentrons_flex_96_tiprack" in labware_name
162+
else None,
163+
)
164+
165+
SNs.append(globals()[f"f_stacker_{s_num}"]._core.get_serial_number())
166+
folder_name = "labware_compatibility_lifetime_test_"
167+
f_name = f'{directory}/{folder_name}{datetime.now().strftime("%m_%d_%y_%H_%M")}.csv'
168+
# ======================= RETRIEVE/STORE TIPRACKS ======================
169+
if not protocol.is_simulating():
170+
with open(f_name, "w", newline="") as csvfile:
171+
test_details = csv.writer(
172+
csvfile, delimiter=",", quotechar='"', quoting=csv.QUOTE_MINIMAL
173+
)
174+
test_details.writerow({"test details"})
175+
test_details.writerow({"Flex Stacker"})
176+
log_file = csv.DictWriter(csvfile, test_data)
177+
log_file.writeheader()
178+
try:
179+
for cycle in range(test_cycles):
180+
labware_list = []
181+
protocol.comment(f"Starting cycle: {cycle} for {labware_name}")
182+
for lc in range(labware_count):
183+
protocol.comment(f"Starting cycle: {cycle} for {labware_name}")
184+
if num_of_stackers == 4:
185+
# ---------------------Retrieve labware Stacker A
186+
lw = globals()[f"f_stacker_{1}"].retrieve()
187+
record_test_data(
188+
test_data,
189+
cycle,
190+
SNs[0],
191+
"retrieve",
192+
labware_name,
193+
lc,
194+
log_file,
195+
csvfile,
196+
)
197+
protocol.move_labware(
198+
lw, globals()[f"f_stacker_{2}"], use_gripper=True
199+
)
200+
# ---------------------Store labware Stacker B
201+
globals()[f"f_stacker_{2}"].store()
202+
record_test_data(
203+
test_data,
204+
cycle,
205+
SNs[1],
206+
"Store",
207+
labware_name,
208+
lc,
209+
log_file,
210+
csvfile,
211+
)
212+
labware_list.append(lw)
213+
# ---------------------Retrieve labware Stacker C
214+
lw = globals()[f"f_stacker_{3}"].retrieve()
215+
record_test_data(
216+
test_data,
217+
cycle,
218+
SNs[2],
219+
"retrieve",
220+
labware_name,
221+
lc,
222+
log_file,
223+
csvfile,
224+
)
225+
protocol.move_labware(
226+
lw, globals()[f"f_stacker_{4}"], use_gripper=True
227+
)
228+
# ---------------------Store labware Stacker D
229+
globals()[f"f_stacker_{4}"].store()
230+
record_test_data(
231+
test_data,
232+
cycle,
233+
SNs[3],
234+
"Store",
235+
labware_name,
236+
lc,
237+
log_file,
238+
csvfile,
239+
)
240+
labware_list.append(lw)
241+
242+
elif num_of_stackers == 2:
243+
# ---------------------Retrieve labware Stacker A
244+
lw = globals()[f"f_stacker_{1}"].retrieve()
245+
record_test_data(
246+
test_data,
247+
cycle,
248+
SNs[0],
249+
"retrieve",
250+
labware_name,
251+
lc,
252+
log_file,
253+
csvfile,
254+
)
255+
protocol.move_labware(
256+
lw, globals()[f"f_stacker_{2}"], use_gripper=True
257+
)
258+
# ---------------------Store labware Stacker B
259+
globals()[f"f_stacker_{2}"].store()
260+
record_test_data(
261+
test_data,
262+
cycle,
263+
SNs[1],
264+
"store",
265+
labware_name,
266+
lc,
267+
log_file,
268+
csvfile,
269+
)
270+
labware_list.append(lw)
271+
272+
for stored_lw in range(labware_count):
273+
protocol.comment(
274+
f"Starting cycle: {stored_lw} for {labware_name}"
275+
)
276+
if num_of_stackers == 4:
277+
# go backwards
278+
# ---------------------Store labware Stacker B
279+
lw = globals()[f"f_stacker_{2}"].retrieve()
280+
record_test_data(
281+
test_data,
282+
cycle,
283+
SNs[1],
284+
"retrieve",
285+
labware_name,
286+
stored_lw,
287+
log_file,
288+
csvfile,
289+
)
290+
protocol.move_labware(
291+
lw, globals()[f"f_stacker_{1}"], use_gripper=True
292+
)
293+
# ---------------------Retrieve labware Stacker A
294+
globals()[f"f_stacker_{1}"].store()
295+
record_test_data(
296+
test_data,
297+
cycle,
298+
SNs[0],
299+
"Store",
300+
labware_name,
301+
stored_lw,
302+
log_file,
303+
csvfile,
304+
)
305+
# go backwards
306+
# ---------------------Store labware Stacker D
307+
lw = globals()[f"f_stacker_{4}"].retrieve()
308+
record_test_data(
309+
test_data,
310+
cycle,
311+
SNs[3],
312+
"retrieve",
313+
labware_name,
314+
stored_lw,
315+
log_file,
316+
csvfile,
317+
)
318+
protocol.move_labware(
319+
lw, globals()[f"f_stacker_{3}"], use_gripper=True
320+
)
321+
# ---------------------Store labware Stacker C
322+
globals()[f"f_stacker_{3}"].store()
323+
record_test_data(
324+
test_data,
325+
cycle,
326+
SNs[2],
327+
"store",
328+
labware_name,
329+
stored_lw,
330+
log_file,
331+
csvfile,
332+
)
333+
334+
elif num_of_stackers == 2:
335+
# go backwards
336+
# ---------------------Store labware Stacker B
337+
lw = globals()[f"f_stacker_{2}"].retrieve()
338+
record_test_data(
339+
test_data,
340+
cycle,
341+
SNs[1],
342+
"retrieve",
343+
labware_name,
344+
stored_lw,
345+
log_file,
346+
csvfile,
347+
)
348+
protocol.move_labware(
349+
lw, globals()[f"f_stacker_{1}"], use_gripper=True
350+
)
351+
# ---------------------Retrieve labware Stacker A
352+
record_test_data(
353+
test_data,
354+
cycle,
355+
SNs[0],
356+
"store",
357+
labware_name,
358+
stored_lw,
359+
log_file,
360+
csvfile,
361+
)
362+
globals()[f"f_stacker_{1}"].store()
363+
364+
except Exception as e:
365+
test_data["Error"] = e
366+
log_file.writerow(test_data)
367+
csvfile.flush()
368+
raise (e)

0 commit comments

Comments
 (0)