-
Notifications
You must be signed in to change notification settings - Fork 178
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): add getNexTip protocol engine command #17038
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks! Code all looks good to me, but I guess I have some fundamental questions.
else: | ||
next_tip = None | ||
|
||
return SuccessData(public=GetNextTipResult(nextTipInfo=next_tip)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks like it'll work as described, but if this command isn't actually doing anything—not affecting state nor making the robot move—I guess it doesn't seem like we should be making it a command?
Like, PAPI can get this functionality by calling state_view.tips.get_next_tip()
. Or some higher-level function built around that, if it's not sufficient on its own. And the HTTP API can expose this through some non-command endpoint, probably /runs/{runId}/currentState
.
Reasons to avoid making it a command include avoiding database compatibility concerns and avoiding increasing our public API surface area. Especially given weirdness about startingWellName
, mentioned below.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, ya, that is a good point.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I get what you're saying, but the thing is that we don't have other well-organized means to do a "structured query", I guess you'd say. It's a component of a pickUpNextTip command, or really pickUpNextTip concept, that will need to be in transfers.
We absolutely could implement it as a public method of the engine, or of an engine state view, and plumb it up through the runner and the orchestrator and the run store and the run to an HTTP route, but that increases our public API surface area also. And this way you get to see it in the runlog.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess I want to pass the question back at you @SyntaxColoring as to whether error recovery or any other EXEC work will need a client to fetch the next tip from engine. We are planning on adding a pickUpNextTip
command sometime later but the critical part for us is knowing which tip to pick up from, and as you correctly gathered, we will be fine using a state getter function for this since it's an internal code that'll need this info. But not sure if a client will need to fetch this info too. Since auto-tip tracking in engine is a feature that is needed by a few projects, a command seemed more useful but maybe we don't need both getNextTip
and pickUpNextTip
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I get what you're saying, but the thing is that we don't have other well-organized means to do a "structured query", I guess you'd say. It's a component of a pickUpNextTip command, or really pickUpNextTip concept, that will need to be in transfers.
Sorry, I'm not following what you mean by "structured query." I might be missing context. I've seen AUTH-1063 and #16602 but I don't see the part that a separate command helps with. Should I be looking somewhere else?
I guess I want to pass the question back at you as to whether error recovery or any other EXEC work will need a client to fetch the next tip from engine.
Possibly "yes," but if so, I am like 90% sure we will want to implement that as a safe, clean, read-only GET
request, instead of running a live command with a POST
request and having to contend with stuff like play/pause/door-open run states.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks great!
tipOriginWell: str = Field( | ||
..., description="The (starting) well name of the next available tip(s)." |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Continuing with Max's point, wouldn't 'origin' be yet another term that means the same thing as 'primary' and 'starting' tip? Is it used somewhere already or will it be a new term? Also the description says 'starting well name..'.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I came up with this with Casey but yeah, I see your point in muddying the waters with another term. I'll change it to tipStartingWell
for consistency
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed with Sanniti but otherwise LGTM, thanks!
public=GetNextTipResult( | ||
nextTipInfo=NoTipAvailable( | ||
noTipReason=NoTipReason.STARTING_TIP_WITH_PARTIAL, | ||
message="Cannot automatically resolve next tip with starting tip and partial tip configuration.", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If this limitation is to match another limitation elsewhere in our codebase, it might be worth a comment here to describe where the underlying limitation comes from, to make things less confusing if the underlying limitation gets fixed.
|
||
if ( | ||
starting_tip_name is not None | ||
and nozzle_map.configuration != NozzleConfigurationType.FULL |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What does NozzleConfigurationType.FULL
mean?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The pipette is configured to pick up all tips on each channel
next_tip = NextTipInfo(labwareId=labware_id, tipStartingWell=well_name) | ||
break | ||
# After the first tip rack is exhausted, starting tip no longer applies | ||
starting_tip_name = None |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hm, so this implementation does not try to remember which tiprack has available tips? It just loops through all the tipracks on every call?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It'll look at all tip racks given, in order, regardless of if it's completely empty. This is similar to how the API code figures out next tip (it never unassigns a tip rack once its been depleted)
decoy.when( | ||
state_view.tips.get_next_tip( | ||
labware_id="123", | ||
num_tips=42, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So in this test, the caller is requesting the next tip for a hypothetical pipette that has 42 tips?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I use that to make it clear that this test is not testing veracity of testing the get_next_tip
logic, but is passing the expected values to this function
starting_tip_name=None, | ||
nozzle_map=mock_nozzle_map, | ||
) | ||
).then_return("foo") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hm, it might be nice if this test could demonstrate the code working when there are multiple labwares and the first one doesn't have any tips left, because I'm kind of curious how your for
-loop in the implementation would work.
Overview
Addresses part of AUTH-1063.
This PR adds a new
getNextTip
protocol engine command, for use in getting next tip information directly from the engine. The command takes in 3 parameters: a pipette ID, a list of labware ID (corresponding to which tip racks to find the next available tip in), and optionally a starting tip in the form of a well name (if not included this automatically resolves to"A1"
, mirroring how the API version of tip tracking works. It's result returns either a newnextTipInfo
object containing the labware ID and well name of the next available tip (or tip to target for non-single channel/single tip configuration pipettes), orNone
if no available tip can be found for that pipette and tip rack(s) combination.Test Plan and Hands on Testing
Tested on robot with Postman, along with unit test coverage.
Changelog
getNextTip
protocol engine command to return information about next available tipRisk assessment
Low, new command that does not affect state.