Skip to content

Commit 6ffd517

Browse files
authored
feat(hardware-testing,api): add basic TOF sensor support to the Flex Stacker diagnostics script. (#17716)
1 parent 12f31d0 commit 6ffd517

File tree

6 files changed

+349
-6
lines changed

6 files changed

+349
-6
lines changed

api/src/opentrons/drivers/flex_stacker/simulator.py

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,10 @@ def __init__(self, serial_number: Optional[str] = None) -> None:
4545
self._tof_registers: Dict[TOFSensor, Dict[int, int]] = {
4646
a: {} for a in TOFSensor
4747
}
48+
self._tof_sensor_status: Dict[TOFSensor, TOFSensorStatus] = {
49+
s: TOFSensorStatus(s, TOFSensorState.IDLE, TOFSensorMode.MEASURE, True)
50+
for s in TOFSensor
51+
}
4852

4953
def set_limit_switch(self, status: LimitSwitchStatus) -> bool:
5054
self._limit_switch_status = status
@@ -116,6 +120,9 @@ async def set_stallguard_threshold(
116120
@ensure_yield
117121
async def enable_tof_sensor(self, sensor: TOFSensor, enable: bool) -> bool:
118122
"""Enable or disable the TOF sensor."""
123+
state = TOFSensorState.IDLE if enable else TOFSensorState.DISABLED
124+
self._tof_sensor_status[sensor].state = state
125+
self._tof_sensor_status[sensor].ok = enable
119126
return True
120127

121128
@ensure_yield
@@ -174,12 +181,7 @@ async def get_tof_driver_register(self, sensor: TOFSensor, reg: int) -> int:
174181
@ensure_yield
175182
async def get_tof_sensor_status(self, sensor: TOFSensor) -> TOFSensorStatus:
176183
"""Get the status of the tof sensor."""
177-
return TOFSensorStatus(
178-
sensor=sensor,
179-
mode=TOFSensorMode.MEASURE,
180-
state=TOFSensorState.IDLE,
181-
ok=True,
182-
)
184+
return self._tof_sensor_status[sensor]
183185

184186
@ensure_yield
185187
async def get_motion_params(self, axis: StackerAxis) -> MoveParams:

api/src/opentrons/drivers/flex_stacker/utils.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
NUMBER_OF_BINS = 10
2+
NUMBER_OF_ZONES = 128
3+
4+
15
def validate_histogram_frame(data: bytes, next_frame_id: int) -> bool:
26
"""Validate Histogram frame, Raise error if invalid."""
37
start_delimn = data[0]

hardware-testing/hardware_testing/modules/flex_stacker_dvt_qc/config.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
test_ui_leds,
1818
test_uv_lockout_switch,
1919
test_install_detection,
20+
test_tof_basic,
21+
test_tof_functional,
2022
)
2123

2224

@@ -32,6 +34,8 @@ class TestSection(enum.Enum):
3234
UI_LEDS = "UI_LEDS"
3335
UV_LOCKOUT_SWITCH = "UV_LOCKOUT_SWITCH"
3436
INSTALL_DETECTION = "INSTALL_DETECTION"
37+
TOF_BASIC = "TOF_BASIC"
38+
TOF_FUNCTIONAL = "TOF_FUNCTIONAL"
3539
Z_AXIS_CURRENT_SPEED = "Z_AXIS_CURRENT_SPEED"
3640
X_AXIS_CURRENT_SPEED = "X_AXIS_CURRENT_SPEED"
3741

@@ -81,6 +85,14 @@ class TestConfig:
8185
TestSection.INSTALL_DETECTION,
8286
test_install_detection.run,
8387
),
88+
(
89+
TestSection.TOF_BASIC,
90+
test_tof_basic.run,
91+
),
92+
(
93+
TestSection.TOF_FUNCTIONAL,
94+
test_tof_functional.run,
95+
),
8496
(
8597
TestSection.Z_AXIS_CURRENT_SPEED,
8698
test_z_axis_current_speed.run,
@@ -133,6 +145,14 @@ def build_report(test_name: str) -> CSVReport:
133145
title=TestSection.INSTALL_DETECTION.value,
134146
lines=test_install_detection.build_csv_lines(),
135147
),
148+
CSVSection(
149+
title=TestSection.TOF_BASIC.value,
150+
lines=test_tof_basic.build_csv_lines(),
151+
),
152+
CSVSection(
153+
title=TestSection.TOF_FUNCTIONAL.value,
154+
lines=test_tof_functional.build_csv_lines(),
155+
),
136156
CSVSection(
137157
title=TestSection.Z_AXIS_CURRENT_SPEED.value,
138158
lines=test_z_axis_current_speed.build_csv_lines(),
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
"""Test TOF Sensor Comms."""
2+
3+
from typing import List, Union
4+
from hardware_testing.data import ui
5+
from hardware_testing.data.csv_report import (
6+
CSVReport,
7+
CSVLine,
8+
CSVLineRepeating,
9+
CSVResult,
10+
)
11+
12+
from .driver import FlexStackerInterface as FlexStacker
13+
from opentrons.drivers.flex_stacker.types import (
14+
Direction,
15+
StackerAxis,
16+
LEDPattern,
17+
TOFSensor,
18+
)
19+
20+
21+
def build_csv_lines() -> List[Union[CSVLine, CSVLineRepeating]]:
22+
"""Build CSV Lines."""
23+
lines: List[Union[CSVLine, CSVLineRepeating]] = [
24+
CSVLine("tof-X-enabled", [CSVResult]),
25+
CSVLine("tof-X-disabled", [CSVResult]),
26+
CSVLine("tof-Z-enabled", [CSVResult]),
27+
CSVLine("tof-Z-disabled", [CSVResult]),
28+
CSVLine(
29+
f"tof-{TOFSensor.X}-histogram",
30+
[CSVResult, str],
31+
),
32+
CSVLine(
33+
f"tof-{TOFSensor.Z}-histogram",
34+
[CSVResult, str],
35+
),
36+
]
37+
return lines
38+
39+
40+
async def tof_sensors_installed(stacker: FlexStacker) -> bool:
41+
"""Check if the tof sensor are installed."""
42+
tof_x = await stacker._driver.get_tof_sensor_status(TOFSensor.X)
43+
tof_z = await stacker._driver.get_tof_sensor_status(TOFSensor.Z)
44+
return tof_x.ok and tof_z.ok
45+
46+
47+
async def test_tof_sensors_for_comms(
48+
stacker: FlexStacker,
49+
sensor: TOFSensor,
50+
enable: bool,
51+
report: CSVReport,
52+
section: str,
53+
) -> None:
54+
"""Test the communication of the tof sensor."""
55+
ui.print_header(f"TOF Sensor - {sensor} sensor.")
56+
# Set the state of the target sensor
57+
await stacker._driver.enable_tof_sensor(sensor, enable)
58+
status = await stacker._driver.get_tof_sensor_status(sensor)
59+
enabled = "enabled" if enable else "disabled"
60+
report(
61+
section,
62+
f"tof-{sensor.name}-{enabled}",
63+
[
64+
CSVResult.from_bool(enable == status.ok),
65+
],
66+
)
67+
68+
69+
async def test_get_tof_sensor_histogram(
70+
stacker: FlexStacker, report: CSVReport, section: str, sensor: TOFSensor
71+
) -> None:
72+
"""Test that we can request and store histogram measurements from this TOF sensor."""
73+
print(f"Getting histogram for {sensor}.")
74+
histogram = await stacker._driver.get_tof_histogram(sensor)
75+
report(
76+
section,
77+
f"tof-{sensor.name}-histogram",
78+
[
79+
CSVResult.PASS,
80+
histogram.bins,
81+
],
82+
)
83+
84+
85+
async def run(stacker: FlexStacker, report: CSVReport, section: str) -> None:
86+
"""Run."""
87+
# Reset LEDs to off
88+
if not stacker._simulating:
89+
ui.get_user_ready("Make sure both TOF sensors are installed.")
90+
ui.get_user_ready("Make sure there is no labware in the stacker.")
91+
await stacker._driver.set_led(0, pattern=LEDPattern.STATIC)
92+
93+
print("Homing stacker X and Z axis.")
94+
await stacker.home_axis(StackerAxis.X, Direction.EXTEND)
95+
await stacker.home_axis(StackerAxis.Z, Direction.RETRACT)
96+
97+
print("Test TOF sensor I2C communication")
98+
# disable the opposite sensor so we can test one at a time
99+
await stacker._driver.enable_tof_sensor(TOFSensor.X, True)
100+
await test_tof_sensors_for_comms(stacker, TOFSensor.X, False, report, section)
101+
await test_tof_sensors_for_comms(stacker, TOFSensor.X, True, report, section)
102+
103+
await stacker._driver.enable_tof_sensor(TOFSensor.Z, True)
104+
await test_tof_sensors_for_comms(stacker, TOFSensor.Z, False, report, section)
105+
await test_tof_sensors_for_comms(stacker, TOFSensor.Z, True, report, section)
106+
107+
print("Test TOF sensor get histogram")
108+
await test_get_tof_sensor_histogram(stacker, report, section, TOFSensor.X)
109+
await test_get_tof_sensor_histogram(stacker, report, section, TOFSensor.Z)
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
"""Test TOF Sensor Functional."""
2+
3+
from typing import List, Union
4+
from hardware_testing.data import ui
5+
from hardware_testing.data.csv_report import (
6+
CSVReport,
7+
CSVLine,
8+
CSVLineRepeating,
9+
CSVResult,
10+
)
11+
from hardware_testing.modules.flex_stacker_dvt_qc.utils import labware_detected
12+
13+
from .driver import FlexStackerInterface as FlexStacker
14+
from opentrons.drivers.flex_stacker.types import (
15+
Direction,
16+
StackerAxis,
17+
LEDPattern,
18+
TOFSensor,
19+
)
20+
21+
22+
def build_csv_lines() -> List[Union[CSVLine, CSVLineRepeating]]:
23+
"""Build CSV Lines."""
24+
lines: List[Union[CSVLine, CSVLineRepeating]] = [
25+
CSVLine(
26+
f"tof-{TOFSensor.X}-histogram-empty",
27+
[bool, str, CSVResult, str],
28+
),
29+
CSVLine(
30+
f"tof-{TOFSensor.Z}-histogram-empty",
31+
[bool, str, CSVResult, str],
32+
),
33+
CSVLine(
34+
f"tof-{TOFSensor.X}-histogram-tiprack",
35+
[bool, str, CSVResult, str],
36+
),
37+
CSVLine(
38+
f"tof-{TOFSensor.Z}-histogram-tiprack",
39+
[bool, str, CSVResult, str],
40+
),
41+
]
42+
return lines
43+
44+
45+
async def tof_sensors_installed(stacker: FlexStacker) -> bool:
46+
"""Check if the tof sensor are installed."""
47+
tof_x = await stacker._driver.get_tof_sensor_status(TOFSensor.X)
48+
tof_z = await stacker._driver.get_tof_sensor_status(TOFSensor.Z)
49+
return tof_x.ok and tof_z.ok
50+
51+
52+
async def test_tof_sensors_labware_detection(
53+
stacker: FlexStacker,
54+
report: CSVReport,
55+
section: str,
56+
sensor: TOFSensor,
57+
labware: str,
58+
) -> None:
59+
"""Test that we can detect labware with the TOF sensor."""
60+
open = not await stacker._driver.get_hopper_door_closed()
61+
if open:
62+
report(
63+
section,
64+
f"tof-{sensor.name}-histogram-{labware}",
65+
[
66+
False,
67+
"HOPPER_OPEN",
68+
CSVResult.FAIL,
69+
[],
70+
],
71+
)
72+
return
73+
74+
print(f"Getting histogram for {sensor}.")
75+
bins = [40, 80]
76+
zones = [0, 1, 2, 3]
77+
status = await stacker._driver.get_tof_sensor_status(sensor)
78+
print(status)
79+
histogram = await stacker._driver.get_tof_histogram(sensor)
80+
detected = not labware_detected(histogram.bins, sensor, bins, zones)
81+
report(
82+
section,
83+
f"tof-{sensor.name}-histogram-{labware}",
84+
[
85+
detected,
86+
"HISTOGRAM",
87+
CSVResult.from_bool(detected),
88+
histogram.bins,
89+
],
90+
)
91+
92+
93+
async def run(stacker: FlexStacker, report: CSVReport, section: str) -> None:
94+
"""Run."""
95+
# Reset LEDs to off
96+
if not stacker._simulating:
97+
ui.get_user_ready("Make sure both TOF sensors are installed.")
98+
await stacker._driver.set_led(0, pattern=LEDPattern.STATIC)
99+
100+
print("Homing stacker X and Z axis.")
101+
await stacker.home_axis(StackerAxis.X, Direction.EXTEND)
102+
await stacker.home_axis(StackerAxis.Z, Direction.RETRACT)
103+
104+
print("Test that we have no labware on the X")
105+
ui.get_user_ready("Make sure there is no labware on the stacker gripper position.")
106+
await test_tof_sensors_labware_detection(
107+
stacker, report, section, TOFSensor.X, "empty"
108+
)
109+
110+
print("Test that we detect tiprack on the X")
111+
ui.get_user_ready("Add 1 tiprack to the stacker X.")
112+
await test_tof_sensors_labware_detection(
113+
stacker, report, section, TOFSensor.X, "tiprack"
114+
)
115+
116+
print("Test that we have no labware on the Z")
117+
ui.get_user_ready(
118+
"Make sure there is no labware in the stacker and close the hopper door."
119+
)
120+
await stacker.close_latch()
121+
await test_tof_sensors_labware_detection(
122+
stacker, report, section, TOFSensor.Z, "empty"
123+
)
124+
125+
print("Test that we detect tiprack on the Z")
126+
ui.get_user_ready("Add 1 tiprack to the stacker Z and close the hopper door.")
127+
await test_tof_sensors_labware_detection(
128+
stacker, report, section, TOFSensor.Z, "tiprack"
129+
)

0 commit comments

Comments
 (0)