Skip to content

Commit 4312b27

Browse files
authored
Merge branch 'edge' into lc-snapshot-protocols-96-happy-path
2 parents 10947e2 + b388983 commit 4312b27

File tree

138 files changed

+7115
-2686
lines changed

Some content is hidden

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

138 files changed

+7115
-2686
lines changed

analyses-snapshot-testing/tests/__snapshots__/analyses_snapshot_test/test_analysis_snapshot[2b866b03f3][Flex_S_v2_21_P1000_96_GRIP_HS_MB_TC_TM_Smoke].json

+5-5
Original file line numberDiff line numberDiff line change
@@ -1265,7 +1265,7 @@
12651265
"cornerOffsetFromSlot": {
12661266
"x": 0,
12671267
"y": 0,
1268-
"z": -0.71
1268+
"z": 0
12691269
},
12701270
"dimensions": {
12711271
"xDimension": 127.7,
@@ -1433,7 +1433,7 @@
14331433
"cornerOffsetFromSlot": {
14341434
"x": 0,
14351435
"y": 0,
1436-
"z": -0.71
1436+
"z": 0
14371437
},
14381438
"dimensions": {
14391439
"xDimension": 127.7,
@@ -1605,7 +1605,7 @@
16051605
"cornerOffsetFromSlot": {
16061606
"x": 0,
16071607
"y": 0,
1608-
"z": -0.71
1608+
"z": 0
16091609
},
16101610
"dimensions": {
16111611
"xDimension": 127.7,
@@ -1781,7 +1781,7 @@
17811781
"cornerOffsetFromSlot": {
17821782
"x": 0,
17831783
"y": 0,
1784-
"z": -0.71
1784+
"z": 0
17851785
},
17861786
"dimensions": {
17871787
"xDimension": 127.7,
@@ -1961,7 +1961,7 @@
19611961
"cornerOffsetFromSlot": {
19621962
"x": 0,
19631963
"y": 0,
1964-
"z": -0.71
1964+
"z": 0
19651965
},
19661966
"dimensions": {
19671967
"xDimension": 127.7,

analyses-snapshot-testing/tests/__snapshots__/analyses_snapshot_test/test_analysis_snapshot[7d16d5dbf0][Flex_X_v2_21_tc_lids_wrong_target].json

+5-5
Original file line numberDiff line numberDiff line change
@@ -2035,7 +2035,7 @@
20352035
"cornerOffsetFromSlot": {
20362036
"x": 0,
20372037
"y": 0,
2038-
"z": -0.71
2038+
"z": 0
20392039
},
20402040
"dimensions": {
20412041
"xDimension": 127.7,
@@ -2202,7 +2202,7 @@
22022202
"cornerOffsetFromSlot": {
22032203
"x": 0,
22042204
"y": 0,
2205-
"z": -0.71
2205+
"z": 0
22062206
},
22072207
"dimensions": {
22082208
"xDimension": 127.7,
@@ -2373,7 +2373,7 @@
23732373
"cornerOffsetFromSlot": {
23742374
"x": 0,
23752375
"y": 0,
2376-
"z": -0.71
2376+
"z": 0
23772377
},
23782378
"dimensions": {
23792379
"xDimension": 127.7,
@@ -2548,7 +2548,7 @@
25482548
"cornerOffsetFromSlot": {
25492549
"x": 0,
25502550
"y": 0,
2551-
"z": -0.71
2551+
"z": 0
25522552
},
25532553
"dimensions": {
25542554
"xDimension": 127.7,
@@ -2727,7 +2727,7 @@
27272727
"cornerOffsetFromSlot": {
27282728
"x": 0,
27292729
"y": 0,
2730-
"z": -0.71
2730+
"z": 0
27312731
},
27322732
"dimensions": {
27332733
"xDimension": 127.7,

analyses-snapshot-testing/tests/__snapshots__/analyses_snapshot_test/test_analysis_snapshot[99c15c6c62][Flex_S_v2_21_tc_lids_happy_path].json

+3-3
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@
121121
"cornerOffsetFromSlot": {
122122
"x": 0,
123123
"y": 0,
124-
"z": -0.71
124+
"z": 0
125125
},
126126
"dimensions": {
127127
"xDimension": 127.7,
@@ -288,7 +288,7 @@
288288
"cornerOffsetFromSlot": {
289289
"x": 0,
290290
"y": 0,
291-
"z": -0.71
291+
"z": 0
292292
},
293293
"dimensions": {
294294
"xDimension": 127.7,
@@ -459,7 +459,7 @@
459459
"cornerOffsetFromSlot": {
460460
"x": 0,
461461
"y": 0,
462-
"z": -0.71
462+
"z": 0
463463
},
464464
"dimensions": {
465465
"xDimension": 127.7,

api/src/opentrons/drivers/asyncio/communication/errors.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ class BaseErrorCode(Enum):
5252
def code_string(self) -> str:
5353
"""Return the error code string."""
5454
code: str = self.value[0]
55-
return code
55+
return code.lower()
5656

5757
@property
5858
def exception(self) -> Type[ErrorResponse]:

api/src/opentrons/hardware_control/motion_utilities.py

+20
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,26 @@ def target_position_from_plunger(
192192
return all_axes_pos
193193

194194

195+
def target_positions_from_plunger_tracking(
196+
mount: Union[Mount, OT3Mount],
197+
plunger_delta: float,
198+
z_delta: float,
199+
current_position: Dict[Axis, float],
200+
) -> "OrderedDict[Axis, float]":
201+
"""Create a target position for machine axes including plungers for dynamic liquid tracking.
202+
203+
The x/y axes remain constant but the plunger and Z move to create a tracking action.
204+
205+
plunger_delta: the distance the plunger should move- should be determined based on desired
206+
volume to aspirate/dispense.
207+
z_delta: the distance to move the z axis- should be determined based on volume and well geometry.
208+
"""
209+
all_axes_pos = target_position_from_plunger(mount, plunger_delta, current_position)
210+
z_ax = Axis.by_mount(mount)
211+
all_axes_pos[z_ax] = current_position[z_ax] + z_delta
212+
return all_axes_pos
213+
214+
195215
def deck_point_from_machine_point(
196216
machine_point: Point, attitude: AttitudeMatrix, offset: Point
197217
) -> Point:

api/src/opentrons/hardware_control/ot3api.py

+97
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,7 @@
127127
target_position_from_absolute,
128128
target_position_from_relative,
129129
target_position_from_plunger,
130+
target_positions_from_plunger_tracking,
130131
offset_for_mount,
131132
deck_from_machine,
132133
machine_from_deck,
@@ -2994,6 +2995,102 @@ async def capacitive_sweep(
29942995

29952996
AMKey = TypeVar("AMKey")
29962997

2998+
async def aspirate_while_tracking(
2999+
self,
3000+
mount: Union[top_types.Mount, OT3Mount],
3001+
z_distance: float,
3002+
volume: float,
3003+
flow_rate: float = 1.0,
3004+
) -> None:
3005+
"""
3006+
Aspirate a volume of liquid (in microliters/uL) while moving the z axis synchronously.
3007+
3008+
:param mount: A robot mount that the instrument is on.
3009+
:param z_distance: The distance the z axis will move during apsiration.
3010+
:param volume: The volume of liquid to be aspirated.
3011+
:param flow_rate: The flow rate to aspirate with.
3012+
"""
3013+
realmount = OT3Mount.from_mount(mount)
3014+
aspirate_spec = self._pipette_handler.plan_check_aspirate(
3015+
realmount, volume, flow_rate
3016+
)
3017+
if not aspirate_spec:
3018+
return
3019+
target_pos = target_positions_from_plunger_tracking(
3020+
realmount,
3021+
aspirate_spec.plunger_distance,
3022+
z_distance,
3023+
self._current_position,
3024+
)
3025+
try:
3026+
await self._backend.set_active_current(
3027+
{aspirate_spec.axis: aspirate_spec.current}
3028+
)
3029+
async with self.restore_system_constrants():
3030+
await self.set_system_constraints_for_plunger_acceleration(
3031+
realmount, aspirate_spec.acceleration
3032+
)
3033+
await self._move(
3034+
target_pos,
3035+
speed=aspirate_spec.speed,
3036+
home_flagged_axes=False,
3037+
)
3038+
except Exception:
3039+
self._log.exception("Aspirate failed")
3040+
aspirate_spec.instr.set_current_volume(0)
3041+
raise
3042+
else:
3043+
aspirate_spec.instr.add_current_volume(aspirate_spec.volume)
3044+
3045+
async def dispense_while_tracking(
3046+
self,
3047+
mount: Union[top_types.Mount, OT3Mount],
3048+
z_distance: float,
3049+
volume: float,
3050+
push_out: Optional[float],
3051+
flow_rate: float = 1.0,
3052+
) -> None:
3053+
"""
3054+
Dispense a volume of liquid (in microliters/uL) while moving the z axis synchronously.
3055+
3056+
:param mount: A robot mount that the instrument is on.
3057+
:param z_distance: The distance the z axis will move during dispensing.
3058+
:param volume: The volume of liquid to be dispensed.
3059+
:param flow_rate: The flow rate to dispense with.
3060+
"""
3061+
realmount = OT3Mount.from_mount(mount)
3062+
dispense_spec = self._pipette_handler.plan_check_dispense(
3063+
realmount, volume, flow_rate, push_out
3064+
)
3065+
if not dispense_spec:
3066+
return
3067+
target_pos = target_positions_from_plunger_tracking(
3068+
realmount,
3069+
dispense_spec.plunger_distance,
3070+
z_distance,
3071+
self._current_position,
3072+
)
3073+
3074+
try:
3075+
await self._backend.set_active_current(
3076+
{dispense_spec.axis: dispense_spec.current}
3077+
)
3078+
async with self.restore_system_constrants():
3079+
await self.set_system_constraints_for_plunger_acceleration(
3080+
realmount, dispense_spec.acceleration
3081+
)
3082+
await self._move(
3083+
target_pos,
3084+
speed=dispense_spec.speed,
3085+
home_flagged_axes=False,
3086+
)
3087+
except Exception:
3088+
self._log.exception("dispense failed")
3089+
dispense_spec.instr.set_current_volume(0)
3090+
raise
3091+
else:
3092+
dispense_spec.instr.remove_current_volume(dispense_spec.volume)
3093+
29973094
@property
29983095
def attached_subsystems(self) -> Dict[SubSystem, SubSystemState]:
29993096
"""Get a view of the state of the currently-attached subsystems."""

api/src/opentrons/hardware_control/protocols/liquid_handler.py

+35
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,23 @@ async def aspirate(
122122
"""
123123
...
124124

125+
async def aspirate_while_tracking(
126+
self,
127+
mount: MountArgType,
128+
z_distance: float,
129+
volume: float,
130+
flow_rate: float = 1.0,
131+
) -> None:
132+
"""
133+
Aspirate a volume of liquid (in microliters/uL) while moving the z axis synchronously.
134+
135+
:param mount: A robot mount that the instrument is on.
136+
:param z_distance: The distance the z axis will move during apsiration.
137+
:param volume: The volume of liquid to be aspirated.
138+
:param flow_rate: The flow rate to aspirate with.
139+
"""
140+
...
141+
125142
async def dispense(
126143
self,
127144
mount: MountArgType,
@@ -143,6 +160,24 @@ async def dispense(
143160
"""
144161
...
145162

163+
async def dispense_while_tracking(
164+
self,
165+
mount: MountArgType,
166+
z_distance: float,
167+
volume: float,
168+
push_out: Optional[float],
169+
flow_rate: float = 1.0,
170+
) -> None:
171+
"""
172+
Dispense a volume of liquid (in microliters/uL) while moving the z axis synchronously.
173+
174+
:param mount: A robot mount that the instrument is on.
175+
:param z_distance: The distance the z axis will move during dispensing.
176+
:param volume: The volume of liquid to be dispensed.
177+
:param flow_rate: The flow rate to dispense with.
178+
"""
179+
...
180+
146181
async def blow_out(
147182
self, mount: MountArgType, volume: Optional[float] = None
148183
) -> None:

api/src/opentrons/protocol_api/_liquid_properties.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,7 @@ def speed(self) -> Optional[float]:
168168

169169
@speed.setter
170170
def speed(self, new_speed: float) -> None:
171-
validated_speed = validation.ensure_positive_float(new_speed)
171+
validated_speed = validation.ensure_greater_than_zero_float(new_speed)
172172
self._speed = validated_speed
173173

174174
def _get_shared_data_params(self) -> Optional[SharedDataTouchTipParams]:

api/src/opentrons/protocol_api/validation.py

+10
Original file line numberDiff line numberDiff line change
@@ -627,6 +627,16 @@ def ensure_positive_float(value: Union[int, float]) -> float:
627627
return float_value
628628

629629

630+
def ensure_greater_than_zero_float(value: Union[int, float]) -> float:
631+
"""Ensure value is a positive and real float value."""
632+
float_value = ensure_float(value)
633+
if isnan(float_value) or isinf(float_value):
634+
raise ValueError("Value must be a defined, non-infinite number.")
635+
if float_value <= 0:
636+
raise ValueError("Value must be a positive float greater than 0.")
637+
return float_value
638+
639+
630640
def ensure_positive_int(value: int) -> int:
631641
"""Ensure value is a positive integer."""
632642
if not isinstance(value, int):

api/src/opentrons/protocol_engine/execution/pipetting.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ async def liquid_probe_in_place(
100100

101101

102102
class HardwarePipettingHandler(PipettingHandler):
103-
"""Liquid handling, using the Hardware API.""" ""
103+
"""Liquid handling, using the Hardware API."""
104104

105105
def __init__(self, state_view: StateView, hardware_api: HardwareControlAPI) -> None:
106106
"""Initialize a PipettingHandler instance."""

api/tests/opentrons/drivers/asyncio/communication/test_serial_connection.py

+8
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,14 @@ def test_raise_on_error(
180180
subject.raise_on_error(response, "fake request")
181181

182182

183+
def test_get_error_codes_lowercase(
184+
subject: SerialKind,
185+
) -> None:
186+
"""It should return an error code dictionary keyed by lowercase value."""
187+
lowercase_result = subject._error_codes.get_error_codes()
188+
assert lowercase_result == {"err003": DefaultErrorCodes.UNHANDLED_GCODE}
189+
190+
183191
async def test_on_retry(mock_serial_port: AsyncMock, subject: SerialKind) -> None:
184192
"""It should try to re-open connection."""
185193
await subject.on_retry()

0 commit comments

Comments
 (0)