Skip to content

Commit 208b89b

Browse files
committed
recover 96ch
1 parent c2e4439 commit 208b89b

File tree

9 files changed

+144
-208
lines changed

9 files changed

+144
-208
lines changed

hardware-testing/hardware_testing/production_qc/ninety_six_assembly_qc_ot3/__main__.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,11 @@ async def _main(cfg: TestConfig) -> None:
1414
# BUILD REPORT
1515
test_name = Path(__file__).parent.name
1616
ui.print_title(test_name.replace("_", " ").upper())
17-
17+
pipette_string = "p1000_96_v3.4" if cfg.pipette == 1000 else "p200_96_v3.0"
1818
# BUILD API
1919
api = await helpers_ot3.build_async_ot3_hardware_api(
2020
is_simulating=cfg.simulate,
21-
pipette_left="p1000_96_v3.4",
21+
pipette_left=pipette_string,
2222
)
2323

2424
# CSV REPORT
@@ -49,7 +49,7 @@ async def _main(cfg: TestConfig) -> None:
4949
# RUN TESTS
5050
for section, test_run in cfg.tests.items():
5151
ui.print_title(section.value)
52-
await test_run(api, report, section.value)
52+
await test_run(api, report, section.value, cfg.pipette)
5353

5454
# RELOAD PIPETTE
5555
ui.print_title("DONE")
@@ -71,6 +71,7 @@ async def _main(cfg: TestConfig) -> None:
7171
parser.add_argument(
7272
f"--only-{s.value.lower()}".replace("_", "-"), action="store_true"
7373
)
74+
parser.add_argument("--pipette", type=int, choices=[200, 1000], default=1000)
7475
args = parser.parse_args()
7576
_t_sections = {
7677
s: f
@@ -87,5 +88,7 @@ async def _main(cfg: TestConfig) -> None:
8788
for s, f in TESTS
8889
if not getattr(args, f"skip_{s.value.lower().replace('-', '_')}")
8990
}
90-
_config = TestConfig(simulate=args.simulate, tests=_t_sections)
91+
_config = TestConfig(
92+
simulate=args.simulate, tests=_t_sections, pipette=args.pipette
93+
)
9194
asyncio.run(_main(_config))

hardware-testing/hardware_testing/production_qc/ninety_six_assembly_qc_ot3/config.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
"""Config."""
22
from dataclasses import dataclass
33
import enum
4-
from typing import Dict, Callable
4+
from typing import Dict, Callable, Literal
55

66
from hardware_testing.data.csv_report import CSVReport, CSVSection
77

@@ -34,6 +34,7 @@ class TestConfig:
3434

3535
simulate: bool
3636
tests: Dict[TestSection, Callable]
37+
pipette: Literal[200, 1000]
3738

3839

3940
TESTS = [

hardware-testing/hardware_testing/production_qc/ninety_six_assembly_qc_ot3/test_capacitance.py

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
"""Test Capacitance."""
22
from asyncio import sleep
3-
from typing import List, Union, Tuple, Optional, cast
3+
from typing import List, Union, Tuple, Optional, cast, Literal
44

55
from opentrons_hardware.hardware_control.tool_sensors import capacitive_probe
66
from opentrons_hardware.firmware_bindings.constants import NodeId, SensorId
@@ -45,6 +45,8 @@
4545
),
4646
}
4747

48+
PROBE_POSITIONS = [InstrumentProbeType.PRIMARY, InstrumentProbeType.SECONDARY]
49+
4850

4951
def _get_test_tag(probe: InstrumentProbeType, reading: str) -> str:
5052
return f"{probe.name.lower()}-{reading}"
@@ -53,7 +55,7 @@ def _get_test_tag(probe: InstrumentProbeType, reading: str) -> str:
5355
def build_csv_lines() -> List[Union[CSVLine, CSVLineRepeating]]:
5456
"""Build CSV Lines."""
5557
lines: List[Union[CSVLine, CSVLineRepeating]] = list()
56-
for p in InstrumentProbeType:
58+
for p in PROBE_POSITIONS:
5759
for r in PROBE_READINGS:
5860
lines.append(CSVLine(_get_test_tag(p, r), [float, CSVResult]))
5961
if "mm" in r:
@@ -104,7 +106,9 @@ def _get_hover_and_probe_pos(
104106
return hover_pos + probe_offset, probe_pos + probe_offset
105107

106108

107-
async def run(api: OT3API, report: CSVReport, section: str) -> None:
109+
async def run(
110+
api: OT3API, report: CSVReport, section: str, pipette: Literal[200, 1000]
111+
) -> None:
108112
"""Run."""
109113
z_ax = Axis.Z_L
110114
p_ax = Axis.P_L
@@ -116,7 +120,7 @@ async def run(api: OT3API, report: CSVReport, section: str) -> None:
116120
if not api.is_simulator:
117121
ui.get_user_ready("REMOVE everything from the deck")
118122

119-
for probe in InstrumentProbeType:
123+
for probe in PROBE_POSITIONS:
120124
# store the thresolds (for reference)
121125
for k in THRESHOLDS.keys():
122126
report(section, _get_test_tag(probe, f"{k}-min"), [THRESHOLDS[k][0]])
@@ -145,7 +149,7 @@ async def run(api: OT3API, report: CSVReport, section: str) -> None:
145149
# ATTACHED-pF
146150
if not api.is_simulator:
147151
ui.get_user_ready(f"ATTACH probe to {probe.name} channel")
148-
await api.add_tip(OT3Mount.LEFT, api.config.calibration.probe_length)
152+
api.add_tip(OT3Mount.LEFT, api.config.calibration.probe_length)
149153
attached_pf = await _read_from_sensor(api, sensor_id, 10)
150154
if not attached_pf:
151155
ui.print_error(f"{probe} cap sensor not working, skipping")
@@ -173,7 +177,7 @@ async def _probe(distance: float, speed: float) -> float:
173177
NodeId.pipette_left,
174178
NodeId.head_l,
175179
distance=distance,
176-
speed=speed,
180+
mount_speed=speed,
177181
sensor_id=sensor_id,
178182
relative_threshold_pf=default_probe_cfg.sensor_threshold_pf,
179183
)
@@ -229,4 +233,4 @@ async def _probe(distance: float, speed: float) -> float:
229233
await api.home_z(OT3Mount.LEFT)
230234
if not api.is_simulator:
231235
ui.get_user_ready("REMOVE probe")
232-
await api.remove_tip(OT3Mount.LEFT)
236+
api.remove_tip(OT3Mount.LEFT)

hardware-testing/hardware_testing/production_qc/ninety_six_assembly_qc_ot3/test_droplets.py

Lines changed: 53 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
"""Test Droplets."""
22
from asyncio import sleep
33
from time import time
4-
from typing import List, Union, Tuple, Optional, Dict
4+
from typing import List, Union, Tuple, Optional, Dict, Literal
55

66
from opentrons.hardware_control.ot3api import OT3API
77
from opentrons.hardware_control.motion_utilities import target_position_from_relative
@@ -16,17 +16,14 @@
1616
from hardware_testing.opentrons_api import helpers_ot3
1717
from hardware_testing.opentrons_api.types import OT3Mount, Point, Axis
1818

19-
TIP_VOLUME = 1000
20-
ASPIRATE_VOLUME = [1000, 5]
2119
NUM_SECONDS_TO_WAIT = 30
2220
HOVER_HEIGHT_MM = 50
2321
DEPTH_INTO_RESERVOIR_FOR_ASPIRATE = -24
2422
DEPTH_INTO_RESERVOIR_FOR_DISPENSE = DEPTH_INTO_RESERVOIR_FOR_ASPIRATE
2523

26-
TIP_RACK_LABWARE = f"opentrons_flex_96_tiprack_{TIP_VOLUME}ul"
2724
RESERVOIR_LABWARE = "nest_1_reservoir_195ml"
2825

29-
TIP_RACK_96_SLOT = 10
26+
TIP_RACK_96_SLOT = 4
3027
TIP_RACK_PARTIAL_SLOT = 5
3128
RESERVOIR_SLOT = 2
3229
TRASH_SLOT = 12
@@ -86,31 +83,31 @@ def get_reservoir_nominal() -> Point:
8683
return reservoir_a1_nominal
8784

8885

89-
def get_tiprack_96_nominal() -> Point:
86+
def get_tiprack_96_nominal(pipette: Literal[200, 1000]) -> Point:
9087
"""Get nominal tiprack position for 96-tip pick-up."""
9188
tip_rack_a1_nominal = helpers_ot3.get_theoretical_a1_position(
92-
TIP_RACK_96_SLOT, TIP_RACK_LABWARE
89+
TIP_RACK_96_SLOT, f"opentrons_flex_96_tiprack_{pipette}ul"
9390
)
9491
return tip_rack_a1_nominal + Point(z=TIP_RACK_96_ADAPTER_HEIGHT)
9592

9693

97-
def get_tiprack_partial_nominal() -> Point:
94+
def get_tiprack_partial_nominal(pipette: Literal[200, 1000]) -> Point:
9895
"""Get nominal tiprack position for partial-tip pick-up."""
9996
tip_rack_a1_nominal = helpers_ot3.get_theoretical_a1_position(
100-
TIP_RACK_PARTIAL_SLOT, TIP_RACK_LABWARE
97+
TIP_RACK_PARTIAL_SLOT, f"opentrons_flex_96_tiprack_{pipette}ul"
10198
)
10299
return tip_rack_a1_nominal
103100

104101

105102
async def aspirate_and_wait(
106-
api: OT3API, reservoir: Point, aspirate_value, seconds: int = 30
103+
api: OT3API, reservoir: Point, volume: int, seconds: int = 30
107104
) -> Tuple[bool, float]:
108105
"""Aspirate and wait."""
109106
await helpers_ot3.move_to_arched_ot3(api, OT3Mount.LEFT, reservoir)
110107
await api.move_to(
111108
OT3Mount.LEFT, reservoir + Point(z=DEPTH_INTO_RESERVOIR_FOR_ASPIRATE)
112109
)
113-
await api.aspirate(OT3Mount.LEFT, aspirate_value)
110+
await api.aspirate(OT3Mount.LEFT, volume)
114111
await api.move_to(OT3Mount.LEFT, reservoir + Point(z=HOVER_HEIGHT_MM))
115112

116113
start_time = time()
@@ -136,16 +133,16 @@ async def aspirate_and_wait(
136133
return result, duration_seconds
137134

138135

139-
async def _drop_tip(api: OT3API, trash: Point) -> None:
136+
async def _drop_tip(api: OT3API, trash: Point, pipette: Literal[200, 1000]) -> None:
140137
print("drop in trash")
141138
await helpers_ot3.move_to_arched_ot3(api, OT3Mount.LEFT, trash + Point(z=20))
142139
await api.move_to(OT3Mount.LEFT, trash)
143140
await api.drop_tip(OT3Mount.LEFT)
144141
# NOTE: a FW bug (as of v14) will sometimes not fully drop tips.
145142
# so here we ask if the operator needs to try again
146-
# while not api.is_simulator and ui.get_user_answer("try dropping again"):
147-
# await api.add_tip(OT3Mount.LEFT, helpers_ot3.get_default_tip_length(TIP_VOLUME))
148-
# await api.drop_tip(OT3Mount.LEFT)
143+
while not api.is_simulator and ui.get_user_answer("try dropping again"):
144+
api.add_tip(OT3Mount.LEFT, helpers_ot3.get_default_tip_length(pipette))
145+
await api.drop_tip(OT3Mount.LEFT)
149146
await api.home_z(OT3Mount.LEFT)
150147

151148

@@ -164,32 +161,36 @@ async def _partial_pick_up_z_motion(
164161
await api._update_position_estimation([Axis.Z_L])
165162

166163

167-
async def _partial_pick_up(api: OT3API, position: Point, current: float) -> None:
164+
async def _partial_pick_up(
165+
api: OT3API, position: Point, current: float, pipette: Literal[200, 1000]
166+
) -> None:
168167
await helpers_ot3.move_to_arched_ot3(
169168
api,
170169
OT3Mount.LEFT,
171170
position,
172171
safe_height=position.z + 10,
173172
)
174173
await _partial_pick_up_z_motion(api, current=current, distance=13, speed=5)
175-
await api.add_tip(OT3Mount.LEFT, helpers_ot3.get_default_tip_length(TIP_VOLUME))
174+
api.add_tip(OT3Mount.LEFT, helpers_ot3.get_default_tip_length(pipette))
176175
await api.prepare_for_aspirate(OT3Mount.LEFT)
177176
await api.home_z(OT3Mount.LEFT)
178177

179178

180-
async def run(api: OT3API, report: CSVReport, section: str) -> None:
179+
async def run(
180+
api: OT3API, report: CSVReport, section: str, pipette: Literal[200, 1000]
181+
) -> None:
181182
"""Run."""
182183
# GATHER NOMINAL POSITIONS
183184
trash_nominal = get_trash_nominal()
184-
tip_rack_96_a1_nominal = get_tiprack_96_nominal()
185+
tip_rack_96_a1_nominal = get_tiprack_96_nominal(pipette)
185186
# tip_rack_partial_a1_nominal = get_tiprack_partial_nominal()
186187
reservoir_a1_nominal = get_reservoir_nominal()
187188
reservoir_a1_actual: Optional[Point] = None
188189

189190
async def _find_reservoir_pos() -> None:
190191
nonlocal reservoir_a1_actual
191-
# if reservoir_a1_actual: # re-find reservoir position for 5ul
192-
# return
192+
if reservoir_a1_actual:
193+
return
193194
# SAVE RESERVOIR POSITION
194195
ui.print_header("JOG to TOP of RESERVOIR")
195196
print("jog tips to the TOP of the RESERVOIR")
@@ -199,37 +200,47 @@ async def _find_reservoir_pos() -> None:
199200
await helpers_ot3.jog_mount_ot3(api, OT3Mount.LEFT)
200201
reservoir_a1_actual = await api.gantry_position(OT3Mount.LEFT)
201202

202-
result = True
203-
for test_volume in ASPIRATE_VOLUME:
204-
answer = ui.get_user_answer(f"Test {test_volume}uL")
205-
if not answer:
206-
continue
207-
tip_volume = 50 if test_volume<=50 else 1000
208-
# PICK-UP 96 TIPS
203+
# PICK-UP 96 TIPS
204+
droplets_result = True
205+
for trial in range(2):
209206
ui.print_header("JOG to 96-Tip RACK")
207+
if trial == 0:
208+
tip_rack = str(pipette) + "ul"
209+
test_volume: int = pipette
210+
else:
211+
tip_rack = "50ul"
212+
test_volume = 1 if pipette == 200 else 5
210213
if not api.is_simulator:
211-
ui.get_user_ready(f"picking up tips, place tip-rack {tip_volume} on slot {TIP_RACK_96_SLOT}")
214+
ui.get_user_ready(f"ADD 96 tip-rack-{tip_rack} to slot #{TIP_RACK_96_SLOT}")
212215
await helpers_ot3.move_to_arched_ot3(
213216
api, OT3Mount.LEFT, tip_rack_96_a1_nominal + Point(z=30)
214217
)
215218
await helpers_ot3.jog_mount_ot3(api, OT3Mount.LEFT)
216-
217-
await api.pick_up_tip(OT3Mount.LEFT, helpers_ot3.get_default_tip_length(tip_volume))
219+
print("picking up tips")
220+
await api.pick_up_tip(
221+
OT3Mount.LEFT, helpers_ot3.get_default_tip_length(pipette)
222+
)
218223
await api.home_z(OT3Mount.LEFT)
219-
if not api.is_simulator:
220-
ui.get_user_ready("about to move to RESERVOIR")
221-
# TEST DROPLETS for 96 TIPS
222-
ui.print_header("96 Tips: ASPIRATE and WAIT")
223-
await _find_reservoir_pos()
224+
if reservoir_a1_actual is None:
225+
if not api.is_simulator:
226+
ui.get_user_ready("about to move to RESERVOIR")
227+
228+
# TEST DROPLETS for 96 TIPS
229+
ui.print_header("96 Tips: ASPIRATE and WAIT")
230+
await _find_reservoir_pos()
224231
assert reservoir_a1_actual
225-
ret, duration = await aspirate_and_wait(
226-
api, reservoir_a1_actual, test_volume, seconds=NUM_SECONDS_TO_WAIT
232+
result, duration = await aspirate_and_wait(
233+
api,
234+
reservoir_a1_actual,
235+
test_volume,
236+
seconds=NUM_SECONDS_TO_WAIT,
227237
)
228-
result = result&ret
238+
droplets_result = droplets_result & result
239+
await _drop_tip(api, trash_nominal, pipette)
229240
await api.home_z(OT3Mount.LEFT)
230-
await _drop_tip(api, trash_nominal)
231-
report(section, "droplets-96-tips", [duration, CSVResult.from_bool(result)])
232-
241+
report(
242+
section, "droplets-96-tips", [duration, CSVResult.from_bool(droplets_result)]
243+
)
233244

234245
# if not api.is_simulator:
235246
# ui.get_user_ready(f"REMOVE 96 tip-rack from slot #{TIP_RACK_96_SLOT}")

hardware-testing/hardware_testing/production_qc/ninety_six_assembly_qc_ot3/test_environmental_sensor.py

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
"""Test Environmental Sensor."""
22
from asyncio import sleep
3-
from typing import List, Union
3+
from typing import List, Union, Literal
44

55
from opentrons.hardware_control.ot3api import OT3API
66

@@ -11,22 +11,19 @@
1111
from hardware_testing.opentrons_api.types import OT3Mount
1212
from hardware_testing.data.csv_report import (
1313
CSVReport,
14-
CSVResult,
1514
CSVLine,
1615
CSVLineRepeating,
1716
)
1817

1918

2019
NUM_SAMPLES = 10
2120
INTER_SAMPLE_DELAY_SECONDS = 0.25
22-
TEMPERATURE_THRESHOLD = [10, 40]
23-
HUMIDITY_THRESHOLD = [10, 90]
2421

2522

2623
def build_csv_lines() -> List[Union[CSVLine, CSVLineRepeating]]:
2724
"""Build CSV Lines."""
2825
return [
29-
CSVLine(f"environment-{sensor_id.name}-celsius-humidity", [float, float, CSVResult])
26+
CSVLine(f"environment-{sensor_id.name}-celsius-humidity", [float, float])
3027
for sensor_id in [SensorId.S0, SensorId.S1]
3128
]
3229

@@ -36,7 +33,9 @@ def _remove_outliers_and_average(values: List[float]) -> float:
3633
return sum(no_outliers) / len(no_outliers)
3734

3835

39-
async def run(api: OT3API, report: CSVReport, section: str) -> None:
36+
async def run(
37+
api: OT3API, report: CSVReport, section: str, pipette: Literal[200, 1000]
38+
) -> None:
4039
"""Run."""
4140
await api.home_z(OT3Mount.LEFT)
4241
slot_5 = helpers_ot3.get_slot_calibration_square_position_ot3(5)
@@ -46,7 +45,6 @@ async def run(api: OT3API, report: CSVReport, section: str) -> None:
4645
ui.print_header(sensor_id.name.upper())
4746
celsius_samples = []
4847
humidity_samples = []
49-
air_params = True
5048
print(f"averaging {NUM_SAMPLES} samples:")
5149
print("\tc\th")
5250
for _ in range(NUM_SAMPLES):
@@ -64,10 +62,8 @@ async def run(api: OT3API, report: CSVReport, section: str) -> None:
6462
humidity = _remove_outliers_and_average(humidity_samples)
6563
print(f"[{sensor_id.name}] Celsius = {round(celsius, 2)} degrees")
6664
print(f"[{sensor_id.name}] Humidity = {round(humidity, 2)} percent")
67-
air_params = air_params & True if TEMPERATURE_THRESHOLD[0] <= celsius <= TEMPERATURE_THRESHOLD[1] and HUMIDITY_THRESHOLD[0] <= humidity <= HUMIDITY_THRESHOLD[1] else False
68-
6965
report(
7066
section,
7167
f"environment-{sensor_id.name}-celsius-humidity",
72-
[celsius, humidity, CSVResult.from_bool(air_params)]
68+
[celsius, humidity],
7369
)

0 commit comments

Comments
 (0)