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

feat(api): Allow recovering from errors that happen during the preparation part of an aspirate command #16896

Merged
merged 14 commits into from
Nov 25, 2024

Conversation

SyntaxColoring
Copy link
Contributor

@SyntaxColoring SyntaxColoring commented Nov 19, 2024

Overview

Closes RQA-3567.

Test Plan and Hands on Testing

  • While the pipette is doing a plunger motion as part of preparation to aspirate, above the well, try inducing an overpressure error and recovering from it using the existing frontend.
    • I have not had any luck inducing an overpressure error here. It seems like the volume moved is too small.
  • Try inducing a stall error during the prepare-to-aspirate step, and recover from it by manually issuing a commandType: "home" intent: "fixit" command.

Changelog

There are 4 substeps in an aspirate command:

  1. Maybe, move above the well.
  2. Maybe, move the plunger to recover from a prior blowOut and prepare for the aspirate.
  3. Move to the target location (normally, into the liquid).
  4. Move the plunger to aspirate the requested volume.

Prior work already lets us recover from overpressure and stall errors in steps 3 and 4. This does the same for steps 1 and 2.

Review requests

Anyone have a trick to triggering an overpressure during the prepare-for-aspirate substep? Is it actually very difficult to trigger, and maybe this is barking up the wrong tree to solve RQA-3567?

Risk assessment

Low.

if isinstance(move_result, DefinedErrorData):
return DefinedErrorData(move_result.public, state_update=state_update)

# TODO: Figure out what to do for state_update_if_false_positive
Copy link
Member

Choose a reason for hiding this comment

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

imo passthrough and union with previous successes

Copy link
Contributor Author

@SyntaxColoring SyntaxColoring Nov 25, 2024

Choose a reason for hiding this comment

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

I think I've settled on ignoring state_update_if_false_positive until/unless this command actually uses it for something, in which case the right thing to do will hopefully be more obvious.

The reason I'm not setting up a pattern of "passthrough and union with previous successes" (yet) is just uncertainty on my part. Like, it kind of smells to me like an Implementation.execute() concern that our inner helper functions aren't in a good position to anticipate. And I wouldn't want to go out of our way to set up a pattern that does nothing now and starts doing the wrong thing later on.

@SyntaxColoring SyntaxColoring changed the title Recoverable prepare to aspirate feat(api): Allow recovering from errors that happen during the preparation part of an aspirate command Nov 20, 2024
Comment on lines +302 to +314
def append(self, other: Self) -> Self:
"""Apply another `StateUpdate` "on top of" this one.

This object is mutated in-place, taking values from `other`.
If an attribute in `other` is `NO_CHANGE`, the value in this object is kept.
"""
fields = dataclasses.fields(other)
for field in fields:
other_value = other.__dict__[field.name]
if other_value != NO_CHANGE:
self.__dict__[field.name] = other_value
return self

Copy link
Contributor Author

@SyntaxColoring SyntaxColoring Nov 20, 2024

Choose a reason for hiding this comment

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

Is append() a good name for this? Is there a naming scheme that harmonizes better with reduce()?

Copy link
Member

Choose a reason for hiding this comment

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

you could make this reduce(), continue to take N arguments, and have self as a silent first argument. that is probably the most natural.

@SyntaxColoring SyntaxColoring marked this pull request as ready for review November 25, 2024 19:58
@SyntaxColoring SyntaxColoring requested a review from a team as a code owner November 25, 2024 19:58
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.

Looks good, minor name suggestion, but lots of extra work probably

Comment on lines +302 to +314
def append(self, other: Self) -> Self:
"""Apply another `StateUpdate` "on top of" this one.

This object is mutated in-place, taking values from `other`.
If an attribute in `other` is `NO_CHANGE`, the value in this object is kept.
"""
fields = dataclasses.fields(other)
for field in fields:
other_value = other.__dict__[field.name]
if other_value != NO_CHANGE:
self.__dict__[field.name] = other_value
return self

Copy link
Member

Choose a reason for hiding this comment

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

you could make this reduce(), continue to take N arguments, and have self as a silent first argument. that is probably the most natural.

@SyntaxColoring SyntaxColoring merged commit 36487d8 into edge Nov 25, 2024
21 checks passed
@SyntaxColoring SyntaxColoring deleted the recoverable_prepare_to_aspirate branch November 25, 2024 20:33
Copy link
Contributor

@TamarZanzouri TamarZanzouri left a comment

Choose a reason for hiding this comment

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

Nice!

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