Skip to content

Commit

Permalink
feat(hardware-testing): add the p200_96 to production qc test scripts (
Browse files Browse the repository at this point in the history
…#16779)

<!--
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
adds a new --pipette [200/1000] arg to the qc test script so we can test
both pipettes
<!--
Describe your PR at a high level. State acceptance criteria and how this
PR fits into other work. Link issues, PRs, and other relevant resources.
-->

## Test Plan and Hands on Testing

<!--
Describe your testing of the PR. Emphasize testing not reflected in the
code. Attach protocols, logs, screenshots and any other assets that
support your testing.
-->

## 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.
-->
  • Loading branch information
ryanthecoder authored Nov 13, 2024
1 parent 8835d1f commit 1a8ea86
Show file tree
Hide file tree
Showing 10 changed files with 57 additions and 36 deletions.
1 change: 1 addition & 0 deletions hardware-testing/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ test-production-qc:
$(python) -m hardware_testing.production_qc.robot_assembly_qc_ot3 --simulate
$(python) -m hardware_testing.production_qc.gripper_assembly_qc_ot3 --simulate
$(python) -m hardware_testing.production_qc.ninety_six_assembly_qc_ot3 --simulate
$(python) -m hardware_testing.production_qc.ninety_six_assembly_qc_ot3 --simulate --pipette 200
$(python) -m hardware_testing.production_qc.stress_test_qc_ot3 --simulate
$(python) -m hardware_testing.production_qc.firmware_check --simulate
$(python) -m hardware_testing.production_qc.belt_calibration_ot3 --simulate
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,11 @@ async def _main(cfg: TestConfig) -> None:
# BUILD REPORT
test_name = Path(__file__).parent.name
ui.print_title(test_name.replace("_", " ").upper())

pipette_string = "p1000_96_v3.4" if cfg.pipette == 1000 else "p200_96_v3.0"
# BUILD API
api = await helpers_ot3.build_async_ot3_hardware_api(
is_simulating=cfg.simulate,
pipette_left="p1000_96_v3.4",
pipette_left=pipette_string,
)

# CSV REPORT
Expand Down Expand Up @@ -49,7 +49,7 @@ async def _main(cfg: TestConfig) -> None:
# RUN TESTS
for section, test_run in cfg.tests.items():
ui.print_title(section.value)
await test_run(api, report, section.value)
await test_run(api, report, section.value, cfg.pipette)

# RELOAD PIPETTE
ui.print_title("DONE")
Expand All @@ -71,6 +71,7 @@ async def _main(cfg: TestConfig) -> None:
parser.add_argument(
f"--only-{s.value.lower()}".replace("_", "-"), action="store_true"
)
parser.add_argument("--pipette", type=int, choices=[200, 1000], default=1000)
args = parser.parse_args()
_t_sections = {
s: f
Expand All @@ -87,5 +88,7 @@ async def _main(cfg: TestConfig) -> None:
for s, f in TESTS
if not getattr(args, f"skip_{s.value.lower().replace('-', '_')}")
}
_config = TestConfig(simulate=args.simulate, tests=_t_sections)
_config = TestConfig(
simulate=args.simulate, tests=_t_sections, pipette=args.pipette
)
asyncio.run(_main(_config))
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
"""Config."""
from dataclasses import dataclass
import enum
from typing import Dict, Callable
from typing import Dict, Callable, Literal

from hardware_testing.data.csv_report import CSVReport, CSVSection

Expand Down Expand Up @@ -34,6 +34,7 @@ class TestConfig:

simulate: bool
tests: Dict[TestSection, Callable]
pipette: Literal[200, 1000]


TESTS = [
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"""Test Capacitance."""
from asyncio import sleep
from typing import List, Union, Tuple, Optional, cast
from typing import List, Union, Tuple, Optional, cast, Literal

from opentrons_hardware.hardware_control.tool_sensors import capacitive_probe
from opentrons_hardware.firmware_bindings.constants import NodeId, SensorId
Expand Down Expand Up @@ -104,7 +104,9 @@ def _get_hover_and_probe_pos(
return hover_pos + probe_offset, probe_pos + probe_offset


async def run(api: OT3API, report: CSVReport, section: str) -> None:
async def run(
api: OT3API, report: CSVReport, section: str, pipette: Literal[200, 1000]
) -> None:
"""Run."""
z_ax = Axis.Z_L
p_ax = Axis.P_L
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
"""Test Droplets."""
from asyncio import sleep
from time import time
from typing import List, Union, Tuple, Optional, Dict
from typing import List, Union, Tuple, Optional, Dict, Literal

from opentrons.hardware_control.ot3api import OT3API
from opentrons.hardware_control.motion_utilities import target_position_from_relative
Expand All @@ -16,14 +16,11 @@
from hardware_testing.opentrons_api import helpers_ot3
from hardware_testing.opentrons_api.types import OT3Mount, Point, Axis

TIP_VOLUME = 1000
ASPIRATE_VOLUME = 1000
NUM_SECONDS_TO_WAIT = 30
HOVER_HEIGHT_MM = 50
DEPTH_INTO_RESERVOIR_FOR_ASPIRATE = -24
DEPTH_INTO_RESERVOIR_FOR_DISPENSE = DEPTH_INTO_RESERVOIR_FOR_ASPIRATE

TIP_RACK_LABWARE = f"opentrons_flex_96_tiprack_{TIP_VOLUME}ul"
RESERVOIR_LABWARE = "nest_1_reservoir_195ml"

TIP_RACK_96_SLOT = 4
Expand Down Expand Up @@ -86,31 +83,31 @@ def get_reservoir_nominal() -> Point:
return reservoir_a1_nominal


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


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


async def aspirate_and_wait(
api: OT3API, reservoir: Point, seconds: int = 30
api: OT3API, reservoir: Point, pipette: Literal[200, 1000], seconds: int = 30
) -> Tuple[bool, float]:
"""Aspirate and wait."""
await helpers_ot3.move_to_arched_ot3(api, OT3Mount.LEFT, reservoir)
await api.move_to(
OT3Mount.LEFT, reservoir + Point(z=DEPTH_INTO_RESERVOIR_FOR_ASPIRATE)
)
await api.aspirate(OT3Mount.LEFT, ASPIRATE_VOLUME)
await api.aspirate(OT3Mount.LEFT, pipette)
await api.move_to(OT3Mount.LEFT, reservoir + Point(z=HOVER_HEIGHT_MM))

start_time = time()
Expand All @@ -136,15 +133,15 @@ async def aspirate_and_wait(
return result, duration_seconds


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

Expand All @@ -164,24 +161,28 @@ async def _partial_pick_up_z_motion(
await api._update_position_estimation([Axis.Z_L])


async def _partial_pick_up(api: OT3API, position: Point, current: float) -> None:
async def _partial_pick_up(
api: OT3API, position: Point, current: float, pipette: Literal[200, 1000]
) -> None:
await helpers_ot3.move_to_arched_ot3(
api,
OT3Mount.LEFT,
position,
safe_height=position.z + 10,
)
await _partial_pick_up_z_motion(api, current=current, distance=13, speed=5)
api.add_tip(OT3Mount.LEFT, helpers_ot3.get_default_tip_length(TIP_VOLUME))
api.add_tip(OT3Mount.LEFT, helpers_ot3.get_default_tip_length(pipette))
await api.prepare_for_aspirate(OT3Mount.LEFT)
await api.home_z(OT3Mount.LEFT)


async def run(api: OT3API, report: CSVReport, section: str) -> None:
async def run(
api: OT3API, report: CSVReport, section: str, pipette: Literal[200, 1000]
) -> None:
"""Run."""
# GATHER NOMINAL POSITIONS
trash_nominal = get_trash_nominal()
tip_rack_96_a1_nominal = get_tiprack_96_nominal()
tip_rack_96_a1_nominal = get_tiprack_96_nominal(pipette)
# tip_rack_partial_a1_nominal = get_tiprack_partial_nominal()
reservoir_a1_nominal = get_reservoir_nominal()
reservoir_a1_actual: Optional[Point] = None
Expand All @@ -208,7 +209,7 @@ async def _find_reservoir_pos() -> None:
)
await helpers_ot3.jog_mount_ot3(api, OT3Mount.LEFT)
print("picking up tips")
await api.pick_up_tip(OT3Mount.LEFT, helpers_ot3.get_default_tip_length(TIP_VOLUME))
await api.pick_up_tip(OT3Mount.LEFT, helpers_ot3.get_default_tip_length(pipette))
await api.home_z(OT3Mount.LEFT)
if not api.is_simulator:
ui.get_user_ready("about to move to RESERVOIR")
Expand All @@ -218,10 +219,13 @@ async def _find_reservoir_pos() -> None:
await _find_reservoir_pos()
assert reservoir_a1_actual
result, duration = await aspirate_and_wait(
api, reservoir_a1_actual, seconds=NUM_SECONDS_TO_WAIT
api,
reservoir_a1_actual,
pipette=pipette,
seconds=NUM_SECONDS_TO_WAIT,
)
report(section, "droplets-96-tips", [duration, CSVResult.from_bool(result)])
await _drop_tip(api, trash_nominal)
await _drop_tip(api, trash_nominal, pipette)

# if not api.is_simulator:
# ui.get_user_ready(f"REMOVE 96 tip-rack from slot #{TIP_RACK_96_SLOT}")
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"""Test Environmental Sensor."""
from asyncio import sleep
from typing import List, Union
from typing import List, Union, Literal

from opentrons.hardware_control.ot3api import OT3API

Expand Down Expand Up @@ -33,7 +33,9 @@ def _remove_outliers_and_average(values: List[float]) -> float:
return sum(no_outliers) / len(no_outliers)


async def run(api: OT3API, report: CSVReport, section: str) -> None:
async def run(
api: OT3API, report: CSVReport, section: str, pipette: Literal[200, 1000]
) -> None:
"""Run."""
await api.home_z(OT3Mount.LEFT)
slot_5 = helpers_ot3.get_slot_calibration_square_position_ot3(5)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
"""Test Jaws."""
from typing import List, Union, Tuple, Dict
from typing import List, Union, Tuple, Dict, Literal

from opentrons.hardware_control.ot3api import OT3API

Expand Down Expand Up @@ -98,7 +98,9 @@ async def jaw_precheck(api: OT3API, ax: Axis, speed: float) -> Tuple[bool, bool]
return led_check, jaws_aligned


async def run(api: OT3API, report: CSVReport, section: str) -> None:
async def run(
api: OT3API, report: CSVReport, section: str, pipette: Literal[200, 1000]
) -> None:
"""Run."""
ax = Axis.Q
settings = helpers_ot3.get_gantry_load_per_axis_motion_settings_ot3(api, ax)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
"""Test Plunger."""
from typing import List, Union, Tuple, Dict
from typing import List, Union, Tuple, Dict, Literal

from opentrons.hardware_control.ot3api import OT3API

Expand Down Expand Up @@ -53,7 +53,9 @@ async def _is_plunger_still_aligned_with_encoder(
return p_enc, p_est, is_aligned


async def run(api: OT3API, report: CSVReport, section: str) -> None:
async def run(
api: OT3API, report: CSVReport, section: str, pipette: Literal[200, 1000]
) -> None:
"""Run."""
ax = Axis.P_L
mount = OT3Mount.LEFT
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"""Test Pressure."""
from asyncio import sleep
from typing import List, Union
from typing import List, Union, Literal

from opentrons_hardware.firmware_bindings.constants import SensorId

Expand Down Expand Up @@ -94,7 +94,9 @@ def check_value(test_value: float, test_name: str) -> CSVResult:
return CSVResult.FAIL


async def run(api: OT3API, report: CSVReport, section: str) -> None:
async def run(
api: OT3API, report: CSVReport, section: str, pipette: Literal[200, 1000]
) -> None:
"""Run."""
await api.home_z(OT3Mount.LEFT)
slot_5 = helpers_ot3.get_slot_calibration_square_position_ot3(5)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"""Test Tip Sensor."""
import asyncio
from typing import List, Union, cast
from typing import List, Union, cast, Literal

from opentrons_hardware.firmware_bindings import ArbitrationId
from opentrons_hardware.firmware_bindings.constants import MessageId
Expand Down Expand Up @@ -75,7 +75,9 @@ def _listener(message: MessageDefinition, arbitration_id: ArbitrationId) -> None
return result


async def run(api: OT3API, report: CSVReport, section: str) -> None:
async def run(
api: OT3API, report: CSVReport, section: str, pipette: Literal[200, 1000]
) -> None:
"""Run."""
ax = Axis.Q
await api.home_z(OT3Mount.LEFT)
Expand Down

0 comments on commit 1a8ea86

Please sign in to comment.