Skip to content

Commit d97002b

Browse files
Merge branch 'edge' into chore_update-pydantic-v2
Integrate with the new mmFromEdge param in TouchTipParams.
2 parents d7b1740 + d69ca2b commit d97002b

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

58 files changed

+1412
-681
lines changed

.github/workflows/api-test-lint-deploy.yaml

+54-2
Original file line numberDiff line numberDiff line change
@@ -73,8 +73,6 @@ jobs:
7373
strategy:
7474
matrix:
7575
os: ['windows-2022', 'ubuntu-22.04', 'macos-latest']
76-
# TODO(mc, 2022-02-24): expand this matrix to 3.8 and 3.9,
77-
# preferably in a nightly cronjob on edge or something
7876
python: ['3.10']
7977
with-ot-hardware: ['true', 'false']
8078
exclude:
@@ -128,6 +126,60 @@ jobs:
128126
files: ./api/coverage.xml
129127
flags: api
130128

129+
test-package:
130+
name: 'installed package tests on ${{ matrix.os }}'
131+
timeout-minutes: 5
132+
strategy:
133+
matrix:
134+
os: ['ubuntu-22.04', 'macos-latest', 'windows-2022']
135+
runs-on: '${{ matrix.os }}'
136+
steps:
137+
- uses: 'actions/checkout@v4'
138+
- name: 'Fix actions/checkout odd handling of tags'
139+
if: startsWith(github.ref, 'refs/tags')
140+
run: |
141+
git fetch -f origin ${{ github.ref }}:${{ github.ref }}
142+
git checkout ${{ github.ref }}
143+
- uses: 'actions/setup-python@v4'
144+
with:
145+
python-version: '3.10'
146+
- name: Set up package-testing
147+
id: setup
148+
if: ${{ matrix.os != 'windows-2022' }}
149+
working-directory: package-testing
150+
shell: bash
151+
run: make setup
152+
- name: Set up package-testing (Windows)
153+
id: setup-windows
154+
if: ${{ matrix.os == 'windows-2022' }}
155+
working-directory: package-testing
156+
shell: pwsh
157+
run: make setup-windows
158+
- name: Run the tests
159+
if: ${{ matrix.os != 'windows-2022' }}
160+
shell: bash
161+
id: test
162+
working-directory: package-testing
163+
run: make test
164+
- name: Run the tests (Windows)
165+
shell: pwsh
166+
id: test-windows
167+
working-directory: package-testing
168+
run: make test-windows
169+
- name: Save the test results
170+
if: ${{ always() && steps.setup.outcome == 'success' || steps.setup-windows.outcome == 'success' }}
171+
id: results
172+
uses: actions/upload-artifact@v4
173+
with:
174+
name: package-test-results-${{ matrix.os }}
175+
path: package-testing/results
176+
- name: Set job summary
177+
if: ${{ always() }}
178+
run: |
179+
echo "## Opentrons Package Test Results ${{matrix.os}}" >> $GITHUB_STEP_SUMMARY
180+
echo "### Test Outcome: Unixy ${{ steps.test.outcome }} Windows: ${{ steps.test-windows.outcome }}" >> $GITHUB_STEP_SUMMARY
181+
echo "[Download the test results artifact](${{steps.results.outputs.artifact-url}})" >> $GITHUB_STEP_SUMMARY
182+
131183
deploy:
132184
name: 'deploy opentrons package'
133185
needs: [test]

.gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -163,3 +163,5 @@ opentrons-robot-app.tar.gz
163163
mock_dir
164164
.npm-cache/
165165
.eslintcache
166+
167+
package-testing/results

api/release-notes-internal.md

+4
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@ For more details about this release, please see the full [technical change log][
22

33
[technical change log]: https://github.com/Opentrons/opentrons/releases
44

5+
## Internal Release 2.3.0-alpha.2
6+
7+
This internal release, pulled from the `edge` branch, contains features being developed for 8.3.0. It's for internal testing only.
8+
59
## Internal Release 2.3.0-alpha.1
610

711
This internal release, pulled from the `edge` branch, contains features being developed for 8.3.0. It's for internal testing only.

api/src/opentrons/protocol_engine/commands/touch_tip.py

+19-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,11 @@
99

1010
from opentrons.types import Point
1111

12-
from ..errors import TouchTipDisabledError, LabwareIsTipRackError
12+
from ..errors import (
13+
TouchTipDisabledError,
14+
TouchTipIncompatibleArgumentsError,
15+
LabwareIsTipRackError,
16+
)
1317
from ..types import DeckPoint
1418
from .command import (
1519
AbstractCommandImpl,
@@ -51,6 +55,13 @@ class TouchTipParams(PipetteIdMixin, WellLocationMixin):
5155
),
5256
)
5357

58+
mmFromEdge: float | SkipJsonSchema[None] = Field(
59+
None,
60+
description="Offset away from the the well edge, in millimeters."
61+
"Incompatible when a radius is included as a non 1.0 value.",
62+
json_schema_extra=_remove_default,
63+
)
64+
5465
speed: float | SkipJsonSchema[None] = Field(
5566
None,
5667
description=(
@@ -96,6 +107,11 @@ async def execute(
96107
labware_id = params.labwareId
97108
well_name = params.wellName
98109

110+
if params.radius != 1.0 and params.mmFromEdge is not None:
111+
raise TouchTipIncompatibleArgumentsError(
112+
"Cannot use mmFromEdge with a radius that is not 1.0"
113+
)
114+
99115
if self._state_view.labware.get_has_quirk(labware_id, "touchTipDisabled"):
100116
raise TouchTipDisabledError(
101117
f"Touch tip not allowed on labware {labware_id}"
@@ -119,11 +135,13 @@ async def execute(
119135
pipette_id, params.speed
120136
)
121137

138+
mm_from_edge = params.mmFromEdge if params.mmFromEdge is not None else 0
122139
touch_waypoints = self._state_view.motion.get_touch_tip_waypoints(
123140
pipette_id=pipette_id,
124141
labware_id=labware_id,
125142
well_name=well_name,
126143
radius=params.radius,
144+
mm_from_edge=mm_from_edge,
127145
center_point=Point(
128146
center_result.public.position.x,
129147
center_result.public.position.y,

api/src/opentrons/protocol_engine/errors/__init__.py

+2
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
LabwareIsTipRackError,
2525
LabwareIsAdapterError,
2626
TouchTipDisabledError,
27+
TouchTipIncompatibleArgumentsError,
2728
WellDoesNotExistError,
2829
PipetteNotLoadedError,
2930
ModuleNotLoadedError,
@@ -110,6 +111,7 @@
110111
"LabwareIsTipRackError",
111112
"LabwareIsAdapterError",
112113
"TouchTipDisabledError",
114+
"TouchTipIncompatibleArgumentsError",
113115
"WellDoesNotExistError",
114116
"PipetteNotLoadedError",
115117
"ModuleNotLoadedError",

api/src/opentrons/protocol_engine/errors/exceptions.py

+13
Original file line numberDiff line numberDiff line change
@@ -361,6 +361,19 @@ def __init__(
361361
super().__init__(ErrorCodes.GENERAL_ERROR, message, details, wrapping)
362362

363363

364+
class TouchTipIncompatibleArgumentsError(ProtocolEngineError):
365+
"""Raised when touch tip is used with both a custom radius and a mmFromEdge argument."""
366+
367+
def __init__(
368+
self,
369+
message: Optional[str] = None,
370+
details: Optional[Dict[str, Any]] = None,
371+
wrapping: Optional[Sequence[EnumeratedError]] = None,
372+
) -> None:
373+
"""Build a TouchTipIncompatibleArgumentsError."""
374+
super().__init__(ErrorCodes.GENERAL_ERROR, message, details, wrapping)
375+
376+
364377
class WellDoesNotExistError(ProtocolEngineError):
365378
"""Raised when referencing a well that does not exist."""
366379

api/src/opentrons/protocol_engine/state/_move_types.py

+9-5
Original file line numberDiff line numberDiff line change
@@ -53,15 +53,19 @@ def get_move_type_to_well(
5353

5454

5555
def get_edge_point_list(
56-
center: Point, x_radius: float, y_radius: float, edge_path_type: EdgePathType
56+
center: Point,
57+
x_radius: float,
58+
y_radius: float,
59+
mm_from_edge: float,
60+
edge_path_type: EdgePathType,
5761
) -> List[Point]:
5862
"""Get list of edge points dependent on edge path type."""
5963
edges = EdgeList(
60-
right=center + Point(x=x_radius, y=0, z=0),
61-
left=center + Point(x=-x_radius, y=0, z=0),
64+
right=center + Point(x=x_radius - mm_from_edge, y=0, z=0),
65+
left=center + Point(x=-x_radius + mm_from_edge, y=0, z=0),
6266
center=center,
63-
forward=center + Point(x=0, y=y_radius, z=0),
64-
back=center + Point(x=0, y=-y_radius, z=0),
67+
forward=center + Point(x=0, y=y_radius - mm_from_edge, z=0),
68+
back=center + Point(x=0, y=-y_radius + mm_from_edge, z=0),
6569
)
6670

6771
if edge_path_type == EdgePathType.LEFT:

api/src/opentrons/protocol_engine/state/motion.py

+6-1
Original file line numberDiff line numberDiff line change
@@ -327,6 +327,7 @@ def get_touch_tip_waypoints(
327327
labware_id: str,
328328
well_name: str,
329329
center_point: Point,
330+
mm_from_edge: float = 0,
330331
radius: float = 1.0,
331332
) -> List[motion_planning.Waypoint]:
332333
"""Get a list of touch points for a touch tip operation."""
@@ -346,7 +347,11 @@ def get_touch_tip_waypoints(
346347
)
347348

348349
positions = _move_types.get_edge_point_list(
349-
center_point, x_offset, y_offset, edge_path_type
350+
center=center_point,
351+
x_radius=x_offset,
352+
y_radius=y_offset,
353+
mm_from_edge=mm_from_edge,
354+
edge_path_type=edge_path_type,
350355
)
351356
critical_point: Optional[CriticalPoint] = None
352357

api/tests/opentrons/protocol_engine/commands/test_touch_tip.py

+109-1
Original file line numberDiff line numberDiff line change
@@ -102,8 +102,99 @@ async def test_touch_tip_implementation(
102102
pipette_id="abc",
103103
labware_id="123",
104104
well_name="A3",
105-
center_point=Point(x=1, y=2, z=3),
106105
radius=0.456,
106+
mm_from_edge=0,
107+
center_point=Point(x=1, y=2, z=3),
108+
)
109+
).then_return(
110+
[
111+
Waypoint(
112+
position=Point(x=11, y=22, z=33),
113+
critical_point=CriticalPoint.XY_CENTER,
114+
),
115+
Waypoint(
116+
position=Point(x=44, y=55, z=66),
117+
critical_point=CriticalPoint.XY_CENTER,
118+
),
119+
]
120+
)
121+
122+
decoy.when(
123+
await mock_gantry_mover.move_to(
124+
pipette_id="abc",
125+
waypoints=[
126+
Waypoint(
127+
position=Point(x=11, y=22, z=33),
128+
critical_point=CriticalPoint.XY_CENTER,
129+
),
130+
Waypoint(
131+
position=Point(x=44, y=55, z=66),
132+
critical_point=CriticalPoint.XY_CENTER,
133+
),
134+
],
135+
speed=9001,
136+
)
137+
).then_return(Point(x=4, y=5, z=6))
138+
139+
result = await subject.execute(params)
140+
141+
assert result == SuccessData(
142+
public=TouchTipResult(position=DeckPoint(x=4, y=5, z=6)),
143+
state_update=update_types.StateUpdate(
144+
pipette_location=update_types.PipetteLocationUpdate(
145+
pipette_id="abc",
146+
new_location=update_types.Well(labware_id="123", well_name="A3"),
147+
new_deck_point=DeckPoint(x=4, y=5, z=6),
148+
)
149+
),
150+
)
151+
152+
153+
async def test_touch_tip_implementation_with_mm_to_edge(
154+
decoy: Decoy,
155+
mock_state_view: StateView,
156+
mock_movement_handler: MovementHandler,
157+
mock_gantry_mover: GantryMover,
158+
subject: TouchTipImplementation,
159+
) -> None:
160+
"""A TouchTip command should use mmFromEdge if provided."""
161+
params = TouchTipParams(
162+
pipetteId="abc",
163+
labwareId="123",
164+
wellName="A3",
165+
wellLocation=WellLocation(offset=WellOffset(x=1, y=2, z=3)),
166+
mmFromEdge=0.789,
167+
speed=42.0,
168+
)
169+
170+
decoy.when(
171+
await mock_movement_handler.move_to_well(
172+
pipette_id="abc",
173+
labware_id="123",
174+
well_name="A3",
175+
well_location=WellLocation(offset=WellOffset(x=1, y=2, z=3)),
176+
current_well=None,
177+
force_direct=False,
178+
minimum_z_height=None,
179+
speed=None,
180+
operation_volume=None,
181+
)
182+
).then_return(Point(x=1, y=2, z=3))
183+
184+
decoy.when(
185+
mock_state_view.pipettes.get_movement_speed(
186+
pipette_id="abc", requested_speed=42.0
187+
)
188+
).then_return(9001)
189+
190+
decoy.when(
191+
mock_state_view.motion.get_touch_tip_waypoints(
192+
pipette_id="abc",
193+
labware_id="123",
194+
well_name="A3",
195+
radius=1.0,
196+
mm_from_edge=0.789,
197+
center_point=Point(x=1, y=2, z=3),
107198
)
108199
).then_return(
109200
[
@@ -183,3 +274,20 @@ async def test_touch_tip_no_tip_racks(
183274

184275
with pytest.raises(errors.LabwareIsTipRackError):
185276
await subject.execute(params)
277+
278+
279+
async def test_touch_tip_incompatible_arguments(
280+
decoy: Decoy, mock_state_view: StateView, subject: TouchTipImplementation
281+
) -> None:
282+
"""It should disallow touch tip if radius and mmFromEdge is provided."""
283+
params = TouchTipParams(
284+
pipetteId="abc",
285+
labwareId="123",
286+
wellName="A3",
287+
wellLocation=WellLocation(),
288+
radius=1.23,
289+
mmFromEdge=4.56,
290+
)
291+
292+
with pytest.raises(errors.TouchTipIncompatibleArgumentsError):
293+
await subject.execute(params)

api/tests/opentrons/protocol_engine/state/test_motion_view.py

+2
Original file line numberDiff line numberDiff line change
@@ -928,6 +928,7 @@ def test_get_touch_tip_waypoints(
928928
x_radius=1.2,
929929
y_radius=3.4,
930930
edge_path_type=_move_types.EdgePathType.RIGHT,
931+
mm_from_edge=0.456,
931932
)
932933
).then_return([Point(x=11, y=22, z=33), Point(x=44, y=55, z=66)])
933934

@@ -937,6 +938,7 @@ def test_get_touch_tip_waypoints(
937938
well_name="B2",
938939
center_point=center_point,
939940
radius=0.123,
941+
mm_from_edge=0.456,
940942
)
941943

942944
assert result == [

0 commit comments

Comments
 (0)