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): Heater Shaker Firmware Level Emulator #11121

Conversation

DerekMaggio
Copy link
Contributor

Overview

Add firmware level emulator and tests for Heater Shaker

Changelog

  • Create HeaterShakerEmulator class
  • Create RPM simulation
  • Add Heater/Shaker settings
  • Add Heater/Shaker to monorepo docker compose file

Review requests

  • @sanni-t & @pmoegenburg can you do a deep dive into whether or not my emulation accurately reproduces the functionality of the hardware Heater/Shaker
  • This is my first PR since being gone on paternity leave, I probably made some stupid errors and style mistakes

Risk assessment

Low

Derek Maggio added 2 commits July 13, 2022 10:21
remove files


Fix some parsing errors


Formatting


Change rpm_per_tick
@DerekMaggio DerekMaggio self-assigned this Jul 13, 2022
@DerekMaggio DerekMaggio marked this pull request as ready for review July 13, 2022 17:25
@DerekMaggio DerekMaggio requested review from a team as code owners July 13, 2022 17:25
@codecov
Copy link

codecov bot commented Jul 13, 2022

Codecov Report

Merging #11121 (cfe7d93) into heater-shaker-firmware-emulator-wip (b4a11af) will not change coverage.
The diff coverage is 100.00%.

Impacted file tree graph

@@                         Coverage Diff                          @@
##           heater-shaker-firmware-emulator-wip   #11121   +/-   ##
====================================================================
  Coverage                                73.76%   73.76%           
====================================================================
  Files                                     2077     2077           
  Lines                                    57372    57372           
  Branches                                  5741     5741           
====================================================================
  Hits                                     42318    42318           
  Misses                                   13816    13816           
  Partials                                  1238     1238           
Flag Coverage Δ
notify-server 89.17% <ø> (ø)

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

Impacted Files Coverage Δ
api/src/opentrons/drivers/utils.py 93.54% <ø> (ø)
...e_control/emulation/scripts/run_module_emulator.py 75.00% <100.00%> (ø)
...c/opentrons/hardware_control/emulation/settings.py 100.00% <100.00%> (ø)
...pentrons/hardware_control/emulation/simulations.py 97.87% <100.00%> (ø)

@DerekMaggio
Copy link
Contributor Author

I confirmed that what I run the docker-compose.yaml file in the monorepo everything starts correctly and I am able to hit the /modules endpoint successfully.

Copy link
Member

@sanni-t sanni-t left a comment

Choose a reason for hiding this comment

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

This is great! Tested the docker-built emulator with dev server and it works! (simply running emulator app with make -C api emulator results in an unrelated error but I think it's been like that for a while now)

Two things I noticed-

  1. Sending a deactivate_shaker makes the heater-shaker in emulator unresponsive for a few seconds. It eventually responds with shake deactivated though so was wondering if that's an intentional silence.
  2. deactivate_heater is not yet implemented. Left out intentionally?

Thanks so much for implementing this so quickly!

Formatting and linting
@DerekMaggio
Copy link
Contributor Author

So after much fruitless investigation, I have no idea why the emulator does not handle the deactivate_shaker command from the Heater/Shaker Hardware Controller.

Had a meeting with Robot Services and they are going to take over the task of getting the command to work.

@DerekMaggio DerekMaggio changed the base branch from edge to heater-shaker-firmware-emulator-wip July 15, 2022 14:58
@DerekMaggio
Copy link
Contributor Author

@sanni-t please note that I have changed the base from edge to heater-shaker-firmware-emulator-wip.
I don't want to merge this to edge until it is completely working

@DerekMaggio
Copy link
Contributor Author

@sanni-t can I get a re-review?

await heatershaker.wait_next_poll()
await heatershaker.start_set_temperature(50.0)

# Have to wait for next poll because target temp will not update until then
Copy link
Contributor

Choose a reason for hiding this comment

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

Is this the intended API contract of the Heater-Shaker hardware controller? If I do hs.start_set_temperature(...) and then do t = hs.target_temperature, I'm not guaranteed to get back what I just set?

If yes: I think this is surprising, and we need to document it in the HeaterShaker class. Doesn't have to happen in this PR, but I'd like confirmation/consensus.

If no: this indicates a bug, I guess?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

You know, it makes sense that target temp should be set and it would be just current temp that hasn't update yet.

Looking into it now.

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 left out a line. Fixing it in a followup commit

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@sanni-t
Copy link
Member

sanni-t commented Jul 20, 2022

The behavior is almost there!
Two things that need to be cleaned up:

  1. On the real hardware, a home command (G28) will only respond back once homing is finished. If we try to do that with the ramp-down behavior of deactivate then it'll take quite a long time, unlike the real hardware that goes from shaking to homed in just 2-3 seconds. So I think it'll be okay to not use the speed ramp down for homing. Instead we can add a delay of 2-3 seconds, set the target and current speeds to zero, and respond with G28 OK.
  2. Ramping down of current temperature on deactivating heater (you probably already know). This one will respond to the gcode immediately after deactivation and then continue ramping down to an ambient temperature.

@DerekMaggio
Copy link
Contributor Author

@sanni-t I addressed your comments here

Is it okay to use a regular sleep in the _home function of the emulator?

@sanni-t
Copy link
Member

sanni-t commented Jul 20, 2022

@sanni-t I addressed your comments here

🙌

Is it okay to use a regular sleep in the _home function of the emulator?

That is a good point. It won't matter for manual testing but it would for the unit tests & automated tests. What do we do for unit tests that need to wait for, say, speed to ramp up? Are we changing the duration of a tick to something very small? In that case, maybe we could modify the tick function to ramp down with rpm_per_tick of something large, like 500.. only when target is 0.. Just a thought

return f"M241 STATUS:{self._latch_status.value.upper()}"

def _deactivate_heater(self, command: Command) -> str:
self._temperature.set_target(TEMPERATURE_ROOM)
Copy link
Member

Choose a reason for hiding this comment

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

This makes the temperature status respond with a target of room temperature value. We want the target to return as None. It looks like the thermocycler & tempdeck's deactivate emulator simply sets the target to None and current temp to room temperature. So, I'm fine with implementing the same thing here for now. We can revisit them all later.

@sanni-t
Copy link
Member

sanni-t commented Jul 20, 2022

That is a good point. It won't matter for manual testing but it would for the unit tests & automated tests. What do we do for unit tests that need to wait for, say, speed to ramp up? Are we changing the duration of a tick to something very small? In that case, maybe we could modify the tick function to ramp down with rpm_per_tick of something large, like 500.. only when target is 0.. Just a thought

Or if that isn't easy to implement, just set the target & current rpm to 0 and send the gcode response. And leave a TODO note to improve this behavior to mimic the hardware's true behavior. We can address all improvements in a follow-up ticket when we're not pressed for time.

@DerekMaggio DerekMaggio reopened this Jul 21, 2022
@sfoster1
Copy link
Member

That is a good point. It won't matter for manual testing but it would for the unit tests & automated tests. What do we do for unit tests that need to wait for, say, speed to ramp up? Are we changing the duration of a tick to something very small? In that case, maybe we could modify the tick function to ramp down with rpm_per_tick of something large, like 500.. only when target is 0.. Just a thought

Or if that isn't easy to implement, just set the target & current rpm to 0 and send the gcode response. And leave a TODO note to improve this behavior to mimic the hardware's true behavior. We can address all improvements in a follow-up ticket when we're not pressed for time.

Let's do the latter. We really need to resist the urge to implement too much behavior here. This is a testing aid and nothing more, and even then because this interacts with a physical system and we're not simulating the world, there's always going to be little places where the behavior doesn't match. We should definitely make the exact format of communications on the wire the same as firmware, like changing an unset target to None; we should definitely not stray into little bits of physical modeling.

@DerekMaggio DerekMaggio requested a review from sanni-t July 21, 2022 14:37
@DerekMaggio
Copy link
Contributor Author

DerekMaggio commented Jul 21, 2022

@sanni-t @sfoster1
I made it so when you call deactivate_heater it now sets the current temp to TEMPERATURE_ROOM and sets target to None.
I also made the home delay time customizable by adding a home_delay_time setting to the HeaterShakerSettings object

Copy link
Member

@sanni-t sanni-t left a comment

Choose a reason for hiding this comment

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

This works as expected! Thank you!

@DerekMaggio DerekMaggio merged commit a98742a into heater-shaker-firmware-emulator-wip Jul 21, 2022
DerekMaggio added a commit that referenced this pull request Jul 25, 2022
* feat: Heater-Shaker Firmware Emulator




remove files


Fix some parsing errors


Formatting


Change rpm_per_tick

* Tests

* Modify home functionality


Formatting and linting

* fix rpm emulator g-code and add deactivate test

* Fix tests

* Add deactivate_heater G-Code

* Add start_set_temperature

* Update home and deactivate_heater functionality

Based off of #11121 (comment)

* Linting

* fix: Fix deactivate_heater function

* Make home_delay_time customizable

* Linting

Co-authored-by: jbleon95 <[email protected]>

Co-authored-by: jbleon95 <[email protected]>
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.

5 participants