Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(api): Raise cases for unsupported nozzle layouts #15009

Merged
merged 4 commits into from
Apr 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions api/src/opentrons/hardware_control/nozzle_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
)
from opentrons_shared_data.errors import ErrorCodes, GeneralError, PythonException

MAXIMUM_NOZZLE_COUNT = 24


def _nozzle_names_by_row(rows: List[PipetteRowDefinition]) -> Iterator[str]:
for row in rows:
Expand Down Expand Up @@ -267,6 +269,17 @@ def build(
(nozzle, physical_nozzles[nozzle]) for nozzle in chain(*rows.values())
)

if (
NozzleConfigurationType.determine_nozzle_configuration(
physical_rows, rows, physical_columns, columns
)
!= NozzleConfigurationType.FULL
):
if len(rows) * len(columns) > MAXIMUM_NOZZLE_COUNT:
raise IncompatibleNozzleConfiguration(
f"Partial Nozzle Layouts may not be configured to contain more than {MAXIMUM_NOZZLE_COUNT} channels."
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

copy suggestion

Suggested change
f"Partial Nozzle Layouts may not be configured to contain more than {MAXIMUM_NOZZLE_COUNT} channels."
f"Partial nozzle layouts can contain at most {MAXIMUM_NOZZLE_COUNT} channels."

)

return cls(
starting_nozzle=starting_nozzle,
map_store=map_store,
Expand Down
21 changes: 14 additions & 7 deletions api/src/opentrons/protocol_api/instrument_context.py
Original file line number Diff line number Diff line change
Expand Up @@ -1928,13 +1928,6 @@ def configure_nozzle_layout(
should be of the same format used when identifying wells by name.
Required unless setting ``style=ALL``.

.. note::
When using the ``COLUMN`` layout, the only fully supported value is
``start="A12"``. You can use ``start="A1"``, but this will disable tip
tracking and you will have to specify the ``location`` every time you
call :py:meth:`.pick_up_tip`, such that the pipette picks up columns of
tips *from right to left* on the tip rack.

:type start: str or ``None``
:param tip_racks: Behaves the same as setting the ``tip_racks`` parameter of
:py:meth:`.load_instrument`. If not specified, the new configuration resets
Expand All @@ -1947,6 +1940,20 @@ def configure_nozzle_layout(
# :param front_right: The nozzle at the front left of the layout. Only used for
# NozzleLayout.QUADRANT configurations.
# :type front_right: str or ``None``
#
# NOTE: Disabled layouts error case can be removed once desired map configurations
# have appropriate data regarding tip-type to map current values added to the
# pipette definitions.
disabled_layouts = [
NozzleLayout.ROW,
NozzleLayout.SINGLE,
NozzleLayout.QUADRANT,
]
if style in disabled_layouts:
raise ValueError(
f"Nozzle layout configuration of style {style.value} is currently unsupported."
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

copy suggestion

Suggested change
f"Nozzle layout configuration of style {style.value} is currently unsupported."
f"{style.value} nozzle layouts are currently not supported."

)

if style != NozzleLayout.ALL:
if start is None:
raise ValueError(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -414,31 +414,31 @@ def test_96_config_identification(
)
== nozzle_manager.NozzleConfigurationType.SUBRECT
)
subject.update_nozzle_configuration("A1", "D12")
subject.update_nozzle_configuration("A1", "B12")
assert (
cast(
nozzle_manager.NozzleConfigurationType,
subject.current_configuration.configuration,
)
== nozzle_manager.NozzleConfigurationType.SUBRECT
)
subject.update_nozzle_configuration("E1", "H12")
subject.update_nozzle_configuration("G1", "H12")
assert (
cast(
nozzle_manager.NozzleConfigurationType,
subject.current_configuration.configuration,
)
== nozzle_manager.NozzleConfigurationType.SUBRECT
)
subject.update_nozzle_configuration("A1", "H6")
subject.update_nozzle_configuration("A1", "H3")
assert (
cast(
nozzle_manager.NozzleConfigurationType,
subject.current_configuration.configuration,
)
== nozzle_manager.NozzleConfigurationType.SUBRECT
)
subject.update_nozzle_configuration("A7", "H12")
subject.update_nozzle_configuration("A10", "H12")
assert (
cast(
nozzle_manager.NozzleConfigurationType,
Expand Down
12 changes: 6 additions & 6 deletions api/tests/opentrons/protocol_engine/state/test_tip_state.py
Original file line number Diff line number Diff line change
Expand Up @@ -1121,17 +1121,17 @@ def _reconfigure_nozzle_layout(start: str, back_l: str, front_r: str) -> NozzleM
)
return configure_nozzle_private_result.nozzle_map

map = _reconfigure_nozzle_layout("A1", "A1", "H10")
_assert_and_pickup("A3", map)
map = _reconfigure_nozzle_layout("A1", "A1", "H3")
_assert_and_pickup("A10", map)
map = _reconfigure_nozzle_layout("A1", "A1", "F2")
_assert_and_pickup("C1", map)
_assert_and_pickup("C8", map)

# Configure to single tip pickups
map = _reconfigure_nozzle_layout("H12", "H12", "H12")
_assert_and_pickup("A1", map)
map = _reconfigure_nozzle_layout("H1", "H1", "H1")
_assert_and_pickup("A2", map)
_assert_and_pickup("A9", map)
map = _reconfigure_nozzle_layout("A12", "A12", "A12")
_assert_and_pickup("B1", map)
_assert_and_pickup("H1", map)
map = _reconfigure_nozzle_layout("A1", "A1", "A1")
_assert_and_pickup("B2", map)
_assert_and_pickup("B9", map)
4 changes: 3 additions & 1 deletion hardware-testing/hardware_testing/gravimetric/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -434,7 +434,9 @@ def _load_pipette(
# NOTE: 8ch QC testing means testing 1 channel at a time,
# so we need to decrease the pick-up current to work with 1 tip.
if pipette.channels == 8 and not increment and not photometric:
pipette.configure_nozzle_layout(NozzleLayout.SINGLE, "A1")
pipette._core.configure_nozzle_layout(
style=NozzleLayout.SINGLE, primary_nozzle="A1", front_right_nozzle="A1"
)
# override deck conflict checking cause we specially lay out our tipracks
DeckConflit.check_safe_for_pipette_movement = (
_override_check_safe_for_pipette_movement
Expand Down
Loading