Skip to content

Conversation

rclarke0
Copy link
Contributor

Overview

Protocol that mixes and handles stalls

Test Plan and Hands on Testing

  • Tested protocol with 8ch 1000 ul, 200 ul tips, and water
  • Initiated stall
  • Watched successful error recovery

Changelog

  • Protocol added to mix
  • If stall occurs, pipette homes the z axis and attempts to blow_out() liquid in the tips
  • If a PositionUnknownError occurs, all of the axises are homed excluding the pipette plungers
  • If no other error occurs, the pipette continues mixing
  • If another error does occur, the error is raised.

Review requests

Risk assessment

  • Protocol will only work if error recovery is disabled on the robot
  • Haven't tested with different liquid classes

@rclarke0 rclarke0 requested a review from JonKlar-OT July 15, 2025 14:00
try:
pipette.aspirate(mix_volume, location.bottom(z=1), flow_rate=mix_flow_rate)
pipette.dispense(mix_volume, location.bottom(z=2), flow_rate=mix_flow_rate)
except Exception as e:
Copy link
Collaborator

Choose a reason for hiding this comment

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

It would be better to catch the error types we are interested in explicitly like:

try:
  ...
except StallOrCollisionDetectedError as err:
  try:
    ...
  except PositionUnkknownError as second_err:
    ...
  finally: 
    ctx.comment(f"Total stalls: {stall_count}")

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I did try catching the errors more explicitly and it did not work.

Copy link
Member

Choose a reason for hiding this comment

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

I don't know that this is going to work because of the way protocol execution happens.

Because the robot needs to talk to the app, the only thing that python protocol api calls do is create protocol engine commands and cause them to run. Any success or failure or state change caused by the commands is reflected by the protocol engine. In some cases, successes or failures of command execution might cause an exception to get eventually thrown in the protocol itself.

However, exceptions are thrown from the engine (i.e. because of protocol failures, rather than like syntax errors) to the protocol basically only for the purpose of causing the python interpreter to stop running the protocol - that's the only way to get the python interpreter to do that. By the time the exception gets thrown, the engine has already seen the error, shown the error to the user, and is actively in the middle of cancelling itself. You can't really handle errors that the engine raises to the protocol.

Because of this, we don't make it easy to catch exceptions - it might make people think that it's a good idea, when probably what it will do is leave the engine in a fairly busted state, where core assumptions about the protocol engine state are not true and things may work but haven't really been tested and will likely fail in surprising ways.

Anyway if you want to catch specific errors from the engine you should catch ProtocolCommandFailedError and check what the original_error field is. Success is really not guaranteed.

Copy link

codecov bot commented Jul 22, 2025

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 24.35%. Comparing base (4c4cb09) to head (dd3efb6).

Additional details and impacted files

Impacted file tree graph

@@           Coverage Diff           @@
##             edge   #18910   +/-   ##
=======================================
  Coverage   24.35%   24.35%           
=======================================
  Files        3359     3359           
  Lines      294605   294605           
  Branches    31029    31029           
=======================================
  Hits        71756    71756           
  Misses     222828   222828           
  Partials       21       21           
Flag Coverage Δ
step-generation 5.32% <ø> (-0.01%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copy link
Member

@sfoster1 sfoster1 left a comment

Choose a reason for hiding this comment

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

I don't think this is a thing that will work super well because of how the engine works (see the inline comment) and won't be reliable if it does work for now, but if it tests out okay maybe it's fine?

try:
pipette.aspirate(mix_volume, location.bottom(z=1), flow_rate=mix_flow_rate)
pipette.dispense(mix_volume, location.bottom(z=2), flow_rate=mix_flow_rate)
except Exception as e:
Copy link
Member

Choose a reason for hiding this comment

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

I don't know that this is going to work because of the way protocol execution happens.

Because the robot needs to talk to the app, the only thing that python protocol api calls do is create protocol engine commands and cause them to run. Any success or failure or state change caused by the commands is reflected by the protocol engine. In some cases, successes or failures of command execution might cause an exception to get eventually thrown in the protocol itself.

However, exceptions are thrown from the engine (i.e. because of protocol failures, rather than like syntax errors) to the protocol basically only for the purpose of causing the python interpreter to stop running the protocol - that's the only way to get the python interpreter to do that. By the time the exception gets thrown, the engine has already seen the error, shown the error to the user, and is actively in the middle of cancelling itself. You can't really handle errors that the engine raises to the protocol.

Because of this, we don't make it easy to catch exceptions - it might make people think that it's a good idea, when probably what it will do is leave the engine in a fairly busted state, where core assumptions about the protocol engine state are not true and things may work but haven't really been tested and will likely fail in surprising ways.

Anyway if you want to catch specific errors from the engine you should catch ProtocolCommandFailedError and check what the original_error field is. Success is really not guaranteed.

@rclarke0 rclarke0 merged commit 82f33e0 into edge Aug 5, 2025
6 checks passed
@rclarke0
Copy link
Contributor Author

rclarke0 commented Aug 5, 2025

I don't think this is a thing that will work super well because of how the engine works (see the inline comment) and won't be reliable if it does work for now, but if it tests out okay maybe it's fine?

I tested it on a robot and it worked

@rclarke0 rclarke0 deleted the peek-no-stall branch August 5, 2025 20:07
neo-jesse pushed a commit that referenced this pull request Aug 19, 2025
<!--
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
Protocol that mixes and handles stalls

## Test Plan and Hands on Testing
- Tested protocol with 8ch 1000 ul, 200 ul tips, and water
- Initiated stall
- Watched successful error recovery

## Changelog

- Protocol added to mix
- If stall occurs, pipette homes the z axis and attempts to blow_out()
liquid in the tips
- If a PositionUnknownError occurs, all of the axises are homed
excluding the pipette plungers
- If no other error occurs, the pipette continues mixing
- If another error does occur, the error is raised.

## Review requests


## Risk assessment

- Protocol will only work if error recovery is disabled on the robot
- Haven't tested with different liquid classes
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants