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): add v4 JSON executor #5221

Merged
merged 74 commits into from
Mar 25, 2020
Merged

feat(api): add v4 JSON executor #5221

merged 74 commits into from
Mar 25, 2020

Conversation

shlokamin
Copy link
Member

@shlokamin shlokamin commented Mar 12, 2020

overview

This PR closes #4868 by adding the v4 JSON executor to the api

changelog

  • Add support for V4 JSON schema in v4 executor
  • Move temperature module await temperature command down to module level
  • Update versioning notes
  • Updated API docs to mention only two pipettes are supported
  • Add start_set_temp and await_temp to run log

review requests

  • Code review
  • Create a protocol with magnetic + temperature modules in PD with their respective steps. Upload to an OT-2 using the run app and validate that the protocol runs smoothly.
  • Test the new temperature module start_set_temperature and await_temperature commands.
  • Test both gen 1 and gen2 modules
  • Make sure commands show up in run log. Note, await temp will show up as a "pause" in the run log
  • Regression test that protocols that do not use modules still behave as expected Nevermind if you export a protocol without modules it will export as a v3 protocol, and therefore use the v3 executor

@codecov
Copy link

codecov bot commented Mar 13, 2020

Codecov Report

Merging #5221 into edge will not change coverage by %.
The diff coverage is n/a.

Impacted file tree graph

@@           Coverage Diff           @@
##             edge    #5221   +/-   ##
=======================================
  Coverage   62.14%   62.14%           
=======================================
  Files        1054     1054           
  Lines       30225    30225           
=======================================
  Hits        18784    18784           
  Misses      11441    11441           

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 312599f...312599f. Read the comment docs.

amitlissack and others added 2 commits March 13, 2020 10:55
)

* create system/wifi module and move some functions from server/endpoints/networking there.

* more refactoring

* code clean up and restructuring.

* move eap option validation from network endpoints to wifi.
rewrote test_key_lifecycle to test the key api. not the http api.

* reworked test_networking_endpoints to only test the http interaction with the key functions. Not the underlying file system. That belongs in the test_wifi module.

* test_network_status added to nmcli. this didn't belong in the server endpoint tests as it is really testing our ability to communicate with nmcli.

* remove deprecated comment

* start robot_server networking endpoints

* progress

* keys is a reserved word. use an alias.

* list keys tests pass

* networking tests pass minus the disconnect

* disconnect tests

* tests for wificonfiguration model validation

* add todo note regarding wifi disconnect

* fix lint errors

* cannot import log_control.MAX_RECORDS due to syslog import failure.

* clean up the instances of multiple response types.

* rename V1ErrorResponse to V1BasicResponse. It isn't always used for errors. Name was misleading.

* fix bad test

* fix lint error

* fix bugs that were revealed when running on PI.
Configure didn't work because we weren't testing how the nmcli.configure call was made.
get wifi/keys failed due to validation errors on pydantic model due to attribute named keys. also, someone (me) didn't port the aiohttp unit tests properly.

closes #5143
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.

Preliminary change requests to make this a bit more idiomatic python and support cancel

return modules_by_id


def _engage_magnet(modules, params) -> None:
Copy link
Member

Choose a reason for hiding this comment

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

We should type the args here. It may be a good idea to make a LoadedModules type variable so we don't have to have Dict[str, ModuleContext] or whatever there all the time

# while holding, because the temperature delta will be exceeded.
# when this flicker happens, this function would sleep for a moment longer

if status == 'heating':
Copy link
Member

Choose a reason for hiding this comment

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

We should push this logic down all the way to the hardware_control.modules.TemperatureModule, so cancelling works here.

The way cancel is currently implemented is in the execution manager in hardware control with an asyncio condition wrapping a state that can be running, paused, or cancelling. When you call a hardware control function that checks the condition (or one of the whitelisted ones for cancellation), you can be cancelled.

As this code stands, the cancellation won't actually happen here - you could be awaiting for a very long time, especially if there's an error in the tempdeck communications.

Making a tempdeck.await_temperature(target: float) function will fix this. We can register it as cancellable with the execution manager, which means it will get cancelled when a cancel happens; or we can implement some more complex logic that pairs monitoring the condition with awaiting the temperature.

The other open question is, what's supposed to happen to the protocol state when this is running? How does it interact with pause? This is as much a UX question as a technical question (@howisthisnamenottakenyet ).

Copy link
Member Author

Choose a reason for hiding this comment

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

Eek, forgot about cancel. I'm fine with moving this down to the module level, that means we're exposing another "hidden" thing to the module context though. But I guess there isn't really another option.

Copy link
Member

Choose a reason for hiding this comment

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

it's really of equal status to the other one though - they come as a pair.

Honestly it's reasonable at this point to unhide it - we wanted to keep it hidden because you could set the temperature, but waiting for it wasn't exposed. If properly waiting for it is exposed, we might as well have it be part of the python api. What do you think @pantslakz and @howisthisnamenottakenyet

asyncio.sleep(0.2)


dispatcher_map: Dict[Any, Any] = {
Copy link
Member

Choose a reason for hiding this comment

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

This map and the code in dispatch_json looks like it will work, but I'd prefer to avoid magic strings in favor of enums - it's a bit easier to type systematically, since we don't have literal types (yet) in mypy. In addition, python enums can have arbitrary objects as values, so you could have something like

class JsonCommandHandler(NamedTuple):  # could also be a typed generic passed down to the handler element (see above re: C++ brain)
   name: str
   handler: Callable[] # appropriately type

class JsonCommand:
   "delay": JsonCommandHandler(...)

api/src/opentrons/protocol_api/execute_v4.py Show resolved Hide resolved
'Valid names (ignoring case): '
'"' + '", "'.join(alias_map.keys()) + '"')
'Valid names (ignoring case): ''"' + '", "'
.join(model_map.keys()) + '", "' + '", "'
Copy link
Member

Choose a reason for hiding this comment

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

This is intended to be a literate error for python protocols; it may be better to avoid mentioning the model_map at all, or to explicitly mention it as a lesser alternative, something like:

"Valid names (ignoring case): {alias_map.keys()}

You can also refer to modules by their exact model: {model_map.keys()}"

@@ -232,6 +232,16 @@ def target(self):
""" Current target temperature in C"""
return self._module.target

@property # type: ignore
@requires_version(2, 0)
Copy link
Member

Choose a reason for hiding this comment

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

This should require version 2.3, since it will be new in 2.3. Also, changes or additions to api-exposed methods should be accompanied with a change note in the version docs


t = self._loop.create_task(_await_temperature(awaiting_temperature))
await self.make_cancellable(t)
return await t
Copy link
Member

Choose a reason for hiding this comment

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

what does this return? if it's nothing, we don't need to return; if it's something, we should annotate the return type in both the inner closure and the wrapper

status = self.status

if status == 'heating':
while (self.temperature < awaiting_temperature):
Copy link
Member

Choose a reason for hiding this comment

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

unnecessary parens

asyncio.sleep(0.2)

elif status == 'cooling':
while (self.temperature > awaiting_temperature):
Copy link
Member

Choose a reason for hiding this comment

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

unnecessary parens

IanLondon and others added 14 commits March 23, 2020 16:51
Clear out engageHeight from MAGNET steps when magnetic module model is
edited.

Closes #5225
…api (#5252)

* robot endpoint progress: home, light, and position implemented with unittests.

* add test for mount with z too low to aiohttp

* move api implemented and unit tested. moved camera into its own router.

* lint issues fixed

* move ThreadAsyncLock into hardware_control package.

* rename to threaded_async_lock and at it to hardware_control export

* use ThreadedAsyncLock in home and move handlers.

* fix lint errors

* add model validator tests. more clean up.

closes #5191
This commit make a new function _do_plunger_home which homes a plunger
on a mount, if it exists, and then moves that plunger to its configured
bottom. This is now used both by API.home_plunger (which was where this
behavior was before) but also by a call to API.home() that involves
pipettes, ensuring that plungers are always moved to the bottom after
they are homed.

Closes #5164
…5187)

Avoids an issue where the ejector would contact the tip rack body.

Closes #5186
…Height (#5262)

Closes #5230

* introduce `.meta` to `HydratedFormData`
@shlokamin shlokamin requested a review from sfoster1 March 24, 2020 15:40
@shlokamin shlokamin requested a review from a team March 24, 2020 17:58
@shlokamin shlokamin requested review from a team as code owners March 24, 2020 17: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.

LGTM once qa approves. In the future we probably shouldn't merge edge back into longstanding branches though, it really makes a mess of the diff.

@shlokamin
Copy link
Member Author

Yeah that's my bad. I saw there was a conflict in the versioning notes so I thought I would merge and fix, but it really just made the tree unintelligible

@shlokamin shlokamin merged commit e81cb56 into edge Mar 25, 2020
@mcous mcous deleted the api_add-v4-json-executor branch May 6, 2020 23:36
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.

Make JSON v4 executor
8 participants