Skip to content

Commit 349092e

Browse files
authored
fix(api, hardware-testing): Fix the flex stacker PVT diagnostics script + increase stallguard 1 -> 2 (#18971)
1 parent 7d4471d commit 349092e

File tree

4 files changed

+89
-86
lines changed

4 files changed

+89
-86
lines changed

api/src/opentrons/hardware_control/modules/flex_stacker.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@
123123
# Stallguard defaults
124124
STALLGUARD_CONFIG = {
125125
StackerAxis.X: StallGuardParams(StackerAxis.X, True, 0),
126-
StackerAxis.Z: StallGuardParams(StackerAxis.Z, True, 0),
126+
StackerAxis.Z: StallGuardParams(StackerAxis.Z, True, 2),
127127
}
128128

129129
# Motion Parameter defaults

hardware-testing/hardware_testing/modules/flex_stacker/flex_stacker_qc/test_stallguard.py

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,22 +12,26 @@
1212
FlexStacker,
1313
STACKER_MOTION_CONFIG,
1414
STALLGUARD_CONFIG,
15+
MAX_TRAVEL,
1516
)
1617
from opentrons_shared_data.errors.exceptions import FlexStackerStallError
1718
from opentrons.drivers.flex_stacker.types import (
1819
StackerAxis,
1920
Direction,
2021
)
2122

22-
# The distance from limit switch to limit switch, mm
23-
TEST_DISTANCE = {StackerAxis.X: 193.5, StackerAxis.Z: 137}
23+
# SGT Test range will be -n 1st index and +n 2nd index
24+
SGT_TEST_RANGE = (2, 3)
25+
# SGT threshold above this value should be ignored
26+
SGT_TEST_CUTOFF = 2
2427

2528

2629
def generate_test_thresholds(test_axis: StackerAxis) -> List[int]:
2730
"""Generate test thresholds."""
2831
thresholds = []
32+
low, high = SGT_TEST_RANGE
2933
default_threshold = STALLGUARD_CONFIG[test_axis].threshold
30-
for sgt in range(default_threshold - 2, default_threshold + 3):
34+
for sgt in range(default_threshold - low, default_threshold + high):
3135
thresholds.append(sgt)
3236
return thresholds
3337

@@ -42,7 +46,7 @@ def build_csv_lines() -> List[Union[CSVLine, CSVLineRepeating]]:
4246
lines: List[Union[CSVLine, CSVLineRepeating]] = []
4347
for axis in [StackerAxis.X, StackerAxis.Z]:
4448
for sgt in generate_test_thresholds(axis):
45-
lines.append(CSVLine(_get_test_tag(axis, sgt), [CSVResult]))
49+
lines.append(CSVLine(_get_test_tag(axis, sgt), [bool, CSVResult]))
4650
return lines
4751

4852

@@ -60,7 +64,7 @@ async def test_stallguard(
6064
await stacker.move_axis(
6165
test_axis,
6266
Direction.RETRACT,
63-
TEST_DISTANCE[test_axis],
67+
MAX_TRAVEL[test_axis],
6468
STACKER_MOTION_CONFIG[test_axis]["move"].move_params.max_speed,
6569
STACKER_MOTION_CONFIG[test_axis]["move"].move_params.acceleration,
6670
STACKER_MOTION_CONFIG[test_axis]["move"].run_current,
@@ -72,10 +76,11 @@ async def test_stallguard(
7276

7377
await stacker.home_axis(test_axis, Direction.EXTEND)
7478

79+
result = stall_detected or sgt > SGT_TEST_CUTOFF
7580
report(
7681
section,
7782
_get_test_tag(test_axis, sgt),
78-
[CSVResult.from_bool(stall_detected)],
83+
[stall_detected, CSVResult.from_bool(result)],
7984
)
8085

8186
# Restore default stallguard threshold

hardware-testing/hardware_testing/scripts/stacker_tof_data_collection.py

Lines changed: 57 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,19 @@
11
"""Flex Stacker TOF Data Collection Script."""
22

3-
# TODO: Make the output of this match tools/tof-analysis/data/raw_data_frame.csv
43
import argparse
54
import asyncio
65
import subprocess
76
import re
87
import time
98
from datetime import datetime
109
from typing import Any, Dict, List, Optional
10+
import uuid
1111

1212
from hardware_testing import data
1313
from hardware_testing.opentrons_api.types import OT3Mount, Axis, Point
1414
from hardware_testing.opentrons_api.helpers_ot3 import build_async_ot3_hardware_api
1515
from opentrons.drivers.flex_stacker.types import StackerAxis, Direction, TOFSensor
16+
from opentrons.drivers.flex_stacker.utils import NUMBER_OF_BINS
1617
from opentrons.hardware_control.ot3api import OT3API
1718

1819

@@ -70,18 +71,33 @@ def __init__(
7071
self.axes = [Axis.X, Axis.Y, Axis.Z_L, Axis.Z_R]
7172
self.stackers: List[str] = []
7273
self.test_files: List[str] = []
74+
7375
self.test_data = {
74-
"Time": "None",
76+
"Hash_id": "None",
77+
"Date": "None",
78+
"Test": "None",
79+
"Labware_Name": "None",
80+
"Stacker_SN": "None",
81+
"Axis": "None",
82+
"Platform_Position": "None",
83+
"Labware_Num_X": "None",
84+
"Labware_Num_Z": "None",
7585
"Sample": "None",
7686
"Zone": "None",
87+
"Time": "None",
7788
}
89+
# Bins 1-128
90+
self.test_data.update(
91+
{str(bin): "None" for bin in range(1, NUMBER_OF_BINS + 1)}
92+
)
93+
7894
self.tof_axes = {
79-
"X-Axis": TOFSensor.X,
80-
"Z-Axis": TOFSensor.Z,
95+
"x": TOFSensor.X,
96+
"z": TOFSensor.Z,
8197
}
8298
self.directions = {
83-
"Retract": Direction.RETRACT,
84-
"Extend": Direction.EXTEND,
99+
"retract": Direction.RETRACT,
100+
"extend": Direction.EXTEND,
85101
}
86102

87103
async def test_setup(self) -> None:
@@ -113,66 +129,32 @@ def file_setup(self) -> None:
113129
self.test_date = "run-" + datetime.utcnow().strftime("%y-%m-%d")
114130
self.test_path = data.create_folder_for_test_data(self.test_name)
115131
if self.labware_amount == 0:
116-
self.labware_name = "LW=baseline"
132+
self.labware_name = "baseline"
117133
self.labware_amount_z = self.labware_amount
118134
elif self.labware_amount == 1:
119-
self.labware_name = "LW=nest-96-pcr"
135+
self.labware_name = "nest-96-pcr"
120136
self.labware_amount_z = self.labware_amount
121137
elif self.labware_amount == 3:
122-
self.labware_name = "LW=tiprack"
138+
self.labware_name = "tiprack"
123139
self.labware_amount = 1
124140
self.labware_amount_z = 3
125141
print("FILE PATH = ", self.test_path)
126142
print("FILE NAMES = ")
127143
for stacker in self.stackers:
128-
self.test_tag_x_ret = f"x-axis_labx{self.labware_amount}_labz{self.labware_amount_z}_retract_{stacker}"
129-
self.test_tag_x_ext = f"x-axis_labx{self.labware_amount}_labz{self.labware_amount_z}_extend_{stacker}"
130-
self.test_tag_z_ret = f"z-axis_labx{self.labware_amount}_labz{self.labware_amount_z}_retract_{stacker}"
131-
self.test_tag_z_ext = f"z-axis_labx{self.labware_amount}_labz{self.labware_amount_z}_extend_{stacker}"
132-
test_file_x_ret = data.create_file_name(
133-
self.labware_name, self.test_id, self.test_tag_x_ret
134-
)
135-
test_file_x_ext = data.create_file_name(
136-
self.labware_name, self.test_id, self.test_tag_x_ext
137-
)
138-
test_file_z_ret = data.create_file_name(
139-
self.labware_name, self.test_id, self.test_tag_z_ret
140-
)
141-
test_file_z_ext = data.create_file_name(
142-
self.labware_name, self.test_id, self.test_tag_z_ext
143-
)
144-
data.append_data_to_file(
145-
test_name=self.test_name,
146-
run_id=self.test_date,
147-
file_name=test_file_x_ret,
148-
data=self.test_header,
144+
self.test_tag = (
145+
f"labx{self.labware_amount}_labz{self.labware_amount_z}_{stacker}"
149146
)
150-
data.append_data_to_file(
151-
test_name=self.test_name,
152-
run_id=self.test_date,
153-
file_name=test_file_x_ext,
154-
data=self.test_header,
147+
test_file = data.create_file_name(
148+
self.labware_name, self.test_id, self.test_tag
155149
)
156150
data.append_data_to_file(
157151
test_name=self.test_name,
158152
run_id=self.test_date,
159-
file_name=test_file_z_ret,
153+
file_name=test_file,
160154
data=self.test_header,
161155
)
162-
data.append_data_to_file(
163-
test_name=self.test_name,
164-
run_id=self.test_date,
165-
file_name=test_file_z_ext,
166-
data=self.test_header,
167-
)
168-
self.test_files.append(test_file_x_ret)
169-
self.test_files.append(test_file_x_ext)
170-
self.test_files.append(test_file_z_ret)
171-
self.test_files.append(test_file_z_ext)
172-
print(test_file_x_ret)
173-
print(test_file_x_ext)
174-
print(test_file_z_ret)
175-
print(test_file_z_ext)
156+
self.test_files.append(test_file)
157+
print(test_file)
176158

177159
def dict_keys_to_line(self, dict: Dict[str, Any]) -> str:
178160
"""Convert dict keys to CSV line."""
@@ -185,6 +167,7 @@ def dict_values_to_line(self, dict: Dict[str, Any]) -> str:
185167
async def read_stacker_tof(self) -> None:
186168
"""Read the stacker TOF Sensor data."""
187169
for i in range(len(self.stackers)):
170+
await self.api.attached_modules[i].home_all() # type: ignore
188171
print(f"\n>> Stacker = {self.stackers[i]}")
189172
for axis, tof_axis in self.tof_axes.items():
190173
for pos, direction in self.directions.items():
@@ -198,30 +181,36 @@ async def read_stacker_tof(self) -> None:
198181
)
199182
hist = await self.api.attached_modules[ # type: ignore
200183
i
201-
]._driver.get_tof_histogram(tof_axis)
184+
]._driver.get_tof_histogram( # type: ignore
185+
tof_axis
186+
)
202187
for zone, bins_list in hist.bins.items():
188+
date = datetime.now().strftime("%Y-%m-%d-%H-%M-%S")
203189
test_data = self.test_data.copy()
204-
test_data["Time"] = str(elapsed_time)
190+
test_data["Hash_id"] = str(uuid.uuid4())
191+
test_data["Date"] = str(date)
192+
test_data["Test"] = self.test_name
193+
test_data["Labware_Name"] = self.labware_name
194+
test_data["Stacker_SN"] = self.stackers[i]
195+
test_data["Axis"] = str(axis.lower())
196+
test_data["Platform_Position"] = pos.lower()
197+
test_data["Labware_Num_X"] = str(self.labware_amount)
198+
test_data["Labware_Num_Z"] = str(self.labware_amount_z)
205199
test_data["Sample"] = str(sample)
206200
test_data["Zone"] = str(zone)
207-
bins_dict = {
208-
index: str(value)
209-
for index, value in enumerate(bins_list)
210-
}
211-
test_data.update(bins_dict) # type: ignore
201+
test_data["Time"] = str(elapsed_time)
202+
# Add the bin values
203+
test_data.update({str(i): str(v) for i, v in enumerate(bins_list, start=1)}) # type: ignore
204+
205+
# Update the csv with new values
212206
test_data_str = self.dict_values_to_line(test_data)
213207
for test_file in self.test_files:
214-
if (
215-
self.stackers[i] in test_file
216-
and axis.lower() in test_file
217-
and pos.lower() in test_file
218-
):
219-
data.append_data_to_file(
220-
test_name=self.test_name,
221-
run_id=self.test_date,
222-
file_name=test_file,
223-
data=test_data_str,
224-
)
208+
data.append_data_to_file(
209+
test_name=self.test_name,
210+
run_id=self.test_date,
211+
file_name=test_file,
212+
data=test_data_str,
213+
)
225214
time.sleep(self.interval)
226215
print("")
227216

hardware-testing/hardware_testing/tools/tof-analysis/tof_analysis.py

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -83,10 +83,10 @@ def _parse_tuple(arg: str) -> Tuple[int, int]:
8383

8484
def _get_value_from_index(deviations: List[int], axis: str, platform: str) -> int:
8585
index_map = {
86-
("X", "extend"): 0,
87-
("X", "retract"): 1,
88-
("Z", "extend"): 2,
89-
("Z", "retract"): 3,
86+
("x", "extend"): 0,
87+
("x", "retract"): 1,
88+
("z", "extend"): 2,
89+
("z", "retract"): 3,
9090
}
9191
return deviations[index_map.get((axis, platform), 0)]
9292

@@ -95,17 +95,20 @@ def _get_tuple_from_index(
9595
bins_list: List[Tuple[int, int]], axis: str, platform: str
9696
) -> Tuple[int, int]:
9797
index_map = {
98-
("X", "extend"): 0,
99-
("X", "retract"): 1,
100-
("Z", "extend"): 2,
101-
("Z", "retract"): 3,
98+
("x", "extend"): 0,
99+
("x", "retract"): 1,
100+
("z", "extend"): 2,
101+
("z", "retract"): 3,
102102
}
103103
return bins_list[index_map.get((axis, platform), 0)]
104104

105105

106106
def convert_to_dict(obj: DefaultDict[Any, Any]) -> Dict[Any, Any]:
107107
"""Convert a defaultdict to dict."""
108-
return {k: convert_to_dict(v) for k, v in obj.items()}
108+
return {
109+
k: convert_to_dict(v) if isinstance(v, defaultdict) else v
110+
for k, v in obj.items()
111+
}
109112

110113

111114
def is_valid_row(axis: str, platform: str, zone: int, config: Dict[Any, Any]) -> bool:
@@ -339,7 +342,7 @@ def _get_visibility_mask(
339342
],
340343
]
341344
fig.update_layout(
342-
title=f"TOF Sensor Baseline: {config['labware_list']} {axis}",
345+
title=f"TOF Sensor Baseline: {config['labware_list']} {axis} {args.graph_name}",
343346
xaxis_title="Bins",
344347
yaxis_title="Photon Count",
345348
template="plotly_white",
@@ -376,7 +379,7 @@ def generate_baseline(args: argparse.Namespace) -> None:
376379

377380
baselines = defaultdict(dict) # type: ignore
378381
for axis, platform_data in histograms.items():
379-
zone_count = zone_count_x if axis == "X" else zone_count_z
382+
zone_count = zone_count_x if axis == "x" else zone_count_z
380383
for platform, zone_data in platform_data.items():
381384
deviation = _get_value_from_index(deviations, axis, platform)
382385
baseline = create_baseline(zone_data, zone_count, bin_count, deviation)
@@ -639,6 +642,12 @@ def main(args: argparse.Namespace) -> None:
639642
type=int,
640643
default=DEFAULT_MAX_SAMPLES,
641644
)
645+
parser.add_argument(
646+
"--graph-name",
647+
help="Optional graph tag to add to the name",
648+
type=str,
649+
default="",
650+
)
642651

643652
args = parser.parse_args()
644653
main(args)

0 commit comments

Comments
 (0)