Skip to content

Commit

Permalink
fix(api): remove gratuitous tip state check prior to drop tip (#13450)
Browse files Browse the repository at this point in the history
Before dropping tip in the hardware controller for ot2 and flex, we previously asserted the pipette
hc tip state is has_tip=False. This provides no extra security as that tip state could be stale. in
the worst case it prevents an intentional command to descend the tip ejector from occuring because
of an inaccurate sw state.

Co-authored-by: jbleon95 <[email protected]>
Co-authored-by: Max Marrone <[email protected]>
  • Loading branch information
3 people authored Oct 9, 2023
1 parent 6347454 commit b18d31f
Show file tree
Hide file tree
Showing 7 changed files with 57 additions and 39 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -896,7 +896,6 @@ def plan_check_drop_tip( # type: ignore[no-untyped-def]
home_after,
):
instrument = self.get_pipette(mount)
self.ready_for_tip_action(instrument, HardwareAction.DROPTIP, mount)

bottom = instrument.plunger_positions.bottom
droptip = instrument.plunger_positions.drop_tip
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -880,7 +880,6 @@ def plan_check_drop_tip(
home_after: bool,
) -> Tuple[DropTipSpec, Callable[[], None]]:
instrument = self.get_pipette(mount)
self.ready_for_tip_action(instrument, HardwareAction.DROPTIP, mount)

is_96_chan = instrument.channels == 96

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,15 +62,6 @@ def test_dispense_no_tip(subject: InstrumentCore) -> None:
)


def test_drop_tip_no_tip(subject: InstrumentCore, tip_rack: LabwareCore) -> None:
"""It should raise an error if a tip is not attached."""
tip_core = tip_rack.get_well_core("A1")

subject.home()
with pytest.raises(UnexpectedTipRemovalError, match="Cannot perform DROPTIP"):
subject.drop_tip(location=None, well_core=tip_core, home_after=False)


def test_blow_out_no_tip(subject: InstrumentCore, labware: LabwareCore) -> None:
"""It should raise an error if a tip is not attached."""
with pytest.raises(UnexpectedTipRemovalError, match="Cannot perform BLOWOUT"):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ stages:
strict:
- json:off

- name: Wait for the protocol to complete
- name: Wait for the run to fail
max_retries: 10
delay_after: 0.1
request:
Expand All @@ -54,16 +54,25 @@ stages:
json:
data:
status: failed

- name: Verify the run contains the expected error
request:
url: '{ot2_server_base_url}/runs/{run_id}'
method: GET
response:
status_code: 200
strict:
- json:off
json:
data:
errors:
- id: !anystr
createdAt: !anystr
errorType: 'UnexpectedTipRemovalError'
detail: 'Cannot perform DROPTIP without a tip attached.'
errorInfo:
mount: 'LEFT'
pipette_name: 'p10_single'
errorCode: '3005'
wrappedErrors: []
errorType: TipNotAttachedError
detail: Pipette should have a tip attached, but does not.
errorInfo: !anydict
wrappedErrors: !anylist
- name: Verify commands contain the expected results
request:
url: '{ot2_server_base_url}/runs/{run_id}/commands'
Expand All @@ -85,7 +94,7 @@ stages:
startedAt: !anystr
completedAt: !anystr
status: succeeded
params: { }
params: {}
- id: !anystr
key: !anystr
commandType: loadLabware
Expand Down Expand Up @@ -114,32 +123,31 @@ stages:
pipetteId: pipetteId
- id: !anystr
key: !anystr
commandType: dropTip
commandType: aspirate
createdAt: !anystr
startedAt: !anystr
completedAt: !anystr
status: failed
error:
id: !anystr
errorType: TipNotAttachedError
createdAt: !anystr
errorType: 'UnexpectedTipRemovalError'
detail: 'Cannot perform DROPTIP without a tip attached.'
errorInfo:
mount: 'LEFT'
pipette_name: 'p10_single'
detail: Pipette should have a tip attached, but does not.
errorCode: '3005'
wrappedErrors: []
errorInfo: !anydict
wrappedErrors: !anylist
params:
pipetteId: pipetteId
labwareId: tipRackId
wellName: A1
wellLocation:
origin: default
origin: bottom
offset:
x: 0
y: 0
z: 0
alternateDropLocation: false
z: 1
flowRate: 3.78
volume: 100
- id: !anystr
key: !anystr
commandType: pickUpTip
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ stages:
strict:
- json:off

- name: Wait for the protocol to complete
- name: Wait for the run to fail
max_retries: 10
delay_after: 0.1
request:
Expand All @@ -54,12 +54,24 @@ stages:
json:
data:
status: failed

- name: Verify the run contains the expected error
request:
url: '{ot2_server_base_url}/runs/{run_id}'
method: GET
response:
status_code: 200
strict:
- json:off
json:
data:
errors:
- id: !anystr
errorType: ExceptionInProtocolError
createdAt: !anystr
detail: 'UnexpectedTipRemovalError [line 9]: Error 3005 UNEXPECTED_TIP_REMOVAL (UnexpectedTipRemovalError): Cannot perform DROPTIP without a tip attached.'
detail: 'UnexpectedTipRemovalError [line 9]: Error 3005 UNEXPECTED_TIP_REMOVAL (UnexpectedTipRemovalError): Cannot perform PREPARE_ASPIRATE without a tip attached.'
errorCode: '4000'
errorInfo: !anydict
wrappedErrors: !anylist

- name: Verify commands contain the expected results
Expand All @@ -82,7 +94,7 @@ stages:
startedAt: !anystr
completedAt: !anystr
status: succeeded
params: { }
params: {}
- id: !anystr
key: !anystr
commandType: loadLabware
Expand All @@ -108,7 +120,7 @@ stages:
mount: right
- id: !anystr
key: !anystr
commandType: dropTip
commandType: aspirate
createdAt: !anystr
startedAt: !anystr
completedAt: !anystr
Expand All @@ -117,7 +129,7 @@ stages:
id: !anystr
errorType: LegacyContextCommandError
createdAt: !anystr
detail: 'Cannot perform DROPTIP without a tip attached.'
detail: 'Cannot perform PREPARE_ASPIRATE without a tip attached.'
errorCode: '3005'
errorInfo: !anydict
wrappedErrors: !anylist
Expand All @@ -126,9 +138,10 @@ stages:
labwareId: !anystr
wellName: A1
wellLocation:
origin: default
origin: top
offset:
x: 0
y: 0
z: 0
alternateDropLocation: false
flowRate: 150
volume: 100
2 changes: 1 addition & 1 deletion robot-server/tests/integration/protocols/runtime_error.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,4 @@
def run(ctx: ProtocolContext) -> None:
tip_rack = ctx.load_labware("opentrons_96_tiprack_300ul", 1)
pipette = ctx.load_instrument("p300_single", "right", [tip_rack])
pipette.drop_tip(tip_rack.wells()[0])
pipette.aspirate(location=tip_rack.wells()[0], volume=100)
12 changes: 10 additions & 2 deletions robot-server/tests/integration/protocols/simple_v6_failure.json
Original file line number Diff line number Diff line change
Expand Up @@ -93,11 +93,19 @@
}
},
{
"commandType": "dropTip",
"commandType": "aspirate",
"params": {
"pipetteId": "pipetteId",
"labwareId": "tipRackId",
"wellName": "A1"
"wellName": "A1",
"volume": 100,
"wellLocation": {
"origin": "bottom",
"offset": {
"z": 1
}
},
"flowRate": 3.78
}
},
{
Expand Down

0 comments on commit b18d31f

Please sign in to comment.