-
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(app, api): map PAPIv2 liquid handling commands to JSON v6 commands #11296
Conversation
Codecov Report
@@ Coverage Diff @@
## edge #11296 +/- ##
===========================================
- Coverage 74.40% 63.59% -10.81%
===========================================
Files 2069 1357 -712
Lines 57258 23401 -33857
Branches 5529 5390 -139
===========================================
- Hits 42601 14883 -27718
+ Misses 13401 7274 -6127
+ Partials 1256 1244 -12
Flags with carried forward coverage won't be shown. Click here to find out more. |
api/tests/opentrons/protocol_runner/smoke_tests/test_legacy_command_mapper.py
Show resolved
Hide resolved
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.
overall code changes look good to me! nice job! just a few small changes.
I have asked robot services for another review bc I am not familiar with the legacy command mapper.
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 would highly recommend splitting up the _build_initial_command
method into multiple private methods. You can even group them by theme- tip_handling_commands and liquid_handling_commands; especially since the aspirate and dispense commands share so much of their code. That will make the code much more readable and reduce unnecessary code size. Moreover, such if-else ladders tend to hide bugs with multiple 'redefinitions' of the same variable (e.g., volume
is defined in both aspirate & dispense. Python linting & error-checking is a little weird/lenient with variable scopes).
Tests won't have to change since they'll all be private methods. So as long as we have public method tests & smoke tests passing, those don't need to be updated.
and "rate" in command["payload"] | ||
): | ||
pipette: LegacyPipetteContext = command["payload"]["instrument"] # type: ignore | ||
location = command["payload"].get("location") |
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'd switch these to accessing directly with keys instead of the get
method. We've already established that these keys exist in lines 325-327 and those params don't have an optional Nonetype in the aspirate/dispense command message types so we won't have to do the checks for None
here at all.
well = location.labware.as_well() | ||
else: | ||
raise Exception("Unknown dispense location.") | ||
parentModuleId = self._module_id_by_slot.get(location.labware.first_parent()) # type: ignore |
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.
Let's make sure we're passing the correct argument type here. labware.first_parent()
can be a string or None while _module_id_by_slot
expects its key to be a DeckSlotName
type.
First, assert that first_parent
is not None
, then cast the string to DeckSlotName
.
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.
Having a bit of troubles. Can you elaborate a bit? I tried the following but getting a lint error:
assert location.labware.first_parent() is not None
firstParent : DeckSlotName = location.labware.first_parent()
parentModuleId = self._module_id_by_slot.get(firstParent)
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.
Try-
first_parent = location.labware.first_parent()
assert first_parent is not None, "Labware needs to be associated with a slot
parent_module_id = self._module_id_by_slot.get(DeckSlotName(first_parent))
Also, just realized, you'll want to make the variable names lowercase and underscored (it's the convention we follow for all python code)
(Updated them in this comment just now)
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.
That works, thank you! I will fix the other areas to be the same as well as fixing the variable names.
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.
We'll also need to add legacy_command_mapper unit tests for aspirate & dispense.
Paired with @jerader to address the above comments and looks like writing a unit test for mapping pipetting commands is not that straight forward. Smoke tests are basically covering the mapping functionality verification so unit tests don't seem to be strictly required. Maybe they'll be easier after the broker refactor. |
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.
In addition to these comments below about never doing raise Exception(...)
in our Python codebase, we're missing tests for the various permutations of the liquid handling commands at different location types and with various parameters specified and unspecified.
We'll need to be pretty confident in our test coverage of this one, because unexpected exceptions during command mapping will not fail gracefully.
I'd recommend writing a Python protocol per command type that executes the PAPIv2 method under test in all possible manner to ensure proper mapping
api/tests/opentrons/protocol_runner/smoke_tests/test_legacy_command_mapper.py
Show resolved
Hide resolved
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.
Still missing test cases, did a more thorough code review and looks like there's some stuff to clear up in the command mapper itself, too. Concentrate on getting the test cases in place before continuing to change the logic code
api/tests/opentrons/protocol_runner/test_legacy_command_mapper.py
Outdated
Show resolved
Hide resolved
api/tests/opentrons/protocol_runner/smoke_tests/test_legacy_command_mapper.py
Show resolved
Hide resolved
f186d24
to
c2d013c
Compare
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.
Just some cleanup comments, but nothing that affects functionality
if location.labware.is_well: | ||
well = location.labware.as_well() |
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.
Can we change this to an assert? The Protocol API ensures that only a well Location
will ever make it here, so the if
is a little misleading, because the else
would be a violation of our internal guarantees
if location.labware.is_well: | |
well = location.labware.as_well() | |
assert location.labware.is_well | |
well = location.labware.as_well() |
You can tell this if
is bad because it's the only place where the well
variable is assigned a value, but it's used later in the function outside of the if
if isinstance(location, LegacyWell): | ||
well = location |
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.
Same comment here; the PAPI is supposed to guarantee this, so we should assert rather than branch
if isinstance(location, LegacyWell): | |
well = location | |
assert isinstance(location, LegacyWell) | |
well = location |
else: | ||
# TODO:(jr, 15.08.2022): aspirate and dispense commands with no specified labware | ||
# get filtered into custom. Refactor this in followup legacy command mapping | ||
return pe_commands.Custom.construct( |
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.
For readability, it might be worth moving this block to the top. I had a lot of trouble tracing this back to its original if
if isinstance(location, Location) and location.labware.is_empty is False: | ||
if location.labware.is_well: |
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.
Should this just be one if
?
if isinstance(location, Location) and location.labware.is_empty is False: | |
if location.labware.is_well: | |
if isinstance(location, Location) and location.labware.is_well: |
Got an approval from Mike
…ds (#11296) closes #11187 & RSS-61 Co-authored-by: Brian Cooper <[email protected]> Co-authored-by: Sakib Hossain <[email protected]> Co-authored-by: smb2268 <[email protected]>
closes #11187 & RSS-61
Overview
Maps
Aspirate
,Dispense
, andBlow_out
to json v6 commands. In order to support pipette access to labware loaded onto modules, this also adds a mapping of labware to module ids.Changelog
Aspirate
,Dispense
, andBlow_out
tolegacy_command_mapper
and add coverage to the smoke testReview requests
aspirate
anddispense
maps correctly and all test cases are included in the smoke testparams tested:
blow_out
maps correctly when there IS NO location included and that test is included in smoke testblow_out
still maps tocustom
when there IS location included and that test is included in smoke test( TODO comments are also included to make sure that we remember to add mapping logic at a future time)console.log
theprotocolData
instepText
, you should see the aspirate, dispense and blow_out show up as their correct commands/commandType
rather thancustom
Risk assessment
low