-
Notifications
You must be signed in to change notification settings - Fork 85
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
Updated Protocol + Scan worker #70
Conversation
Thanks for the heads up, I'll be on a plane the next day(s), can jump back into this not earlier than maybe Tuesday or Wednesday.
What's the Ubuntu 16.04 issue with Python? Check the open PRs there should be one with Docker (sorry on a phone). You could use a local 16.04 container and try to reproduce.
…On April 14, 2017 4:00:16 PM PDT, dcyoung ***@***.***> wrote:
This PR brings a few changes:
1. Updated protocol to match the new firmware that is installed on
devices shipping out today.
2. Updated to perform checks for status byte codes indicating failures
that do not result in errors.
3. Provide methods (and update existing methods) to wait until the
device is ready before attempting to change motor speed or start
scanning.
4. Add scan worker to accumulate scans in a queue using a background
thread.
5. Return complete scans between 0-360 degrees.
6. Offer alternative non-blocking functions where possible (currently
only available in C library).
I have tested the examples, and the bindings on windows. I have tested
the examples and bindings on ubuntu 14.04, but @kent-williams mentioned
he might have experienced an issue with the python bindings on 16.04.
The travis builds might not indicate anything as they're using the
dummy build. So i'd love to get additional testing feedback if you have
the time @daniel-j-h
This PR needs get merged ASAP, as people will be receiving devices very
soon (early/mid next week). I'll go ahead and merge this in a few days
if there aren't any complaints. But please add your feedback if you
have it!
You can view, comment on, or merge this pull request online at:
#70
-- Commit Summary --
* Protocol and SDK updated to check for Motor Stabilization
* Added blocking and non blocking verisons of functions that fail when
motor is not ready
* Accumulate scans in a detached background thread
* Background scan worker thread accumulates entire ordered scan
* Updated doc to reflect change in Motor ready command symbols
-- File Changes --
M libsweep/README.md (63)
M libsweep/doc/serial_protocol_spec.md (124)
M libsweep/examples/example.c (7)
M libsweep/examples/example.cc (8)
M libsweep/examples/net.cc (1)
M libsweep/examples/viewer.cc (1)
M libsweep/include/protocol.h (22)
M libsweep/include/sweep/sweep.h (34)
M libsweep/include/sweep/sweep.hpp (27)
M libsweep/src/dummy.cc (151)
M libsweep/src/protocol.cc (37)
M libsweep/src/sweep.cc (455)
M sweepjs/README.md (8)
M sweepjs/index.js (3)
M sweepjs/sweepjs.cc (24)
M sweepjs/sweepjs.h (2)
M sweeppy/README.md (6)
M sweeppy/sweeppy/__init__.py (26)
M sweeppy/sweeppy/__main__.py (4)
-- Patch Links --
https://github.com/scanse/sweep-sdk/pull/70.patch
https://github.com/scanse/sweep-sdk/pull/70.diff
|
Signals the `sweep_device_s` to start scanning. | ||
Will only succeed if the motor speed is \>0Hz and stable. | ||
User is responsible for checking these conditions before calling. User can check that motor speed has stabilized using `sweep_device_get_motor_ready` and that motor speed is \> 0Hz using `sweep_device_get_speed`. | ||
Will NOT start an internal background thread. User is responsible for keeping up with incoming scans by calling `sweep_device_get_scan_direct`. |
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 why do we mix waiting for motor to stabilize and worker thread here? (And in the PR in general)
Shouldn't the two be orthogonal concepts?
What I'm a bit worried about is the API is starting to get much more confusing than just start scanning - get scans.
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 idea behind the non-blocking versions of certain methods, was to provide options for low-level development, where blocking or threading wasn't available.
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've gone ahead and removed the alternative methods from the API for now. We can merge this PR, and worry about the naming conventions etc later. The only changes to the public API will be the addition of two new methods (get_motor_ready
and wait_until_motor_ready
). As @MikeGitb suggested, we can work on the implementation details and naming conventions for the alternative bits AFTER this PR... and merge the updated API later when we're comfortable with it.
libsweep/README.md
Outdated
void sweep_device_stop_scanning(sweep_device_s device, sweep_error_s* error) | ||
``` | ||
|
||
Signals the `sweep_device_s` to stop scanning. | ||
Blocks for ~20ms to allow time for trailing data stream to collect and flush internally. |
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.
Does this depend on motor speed? 20ms is the upper bound?
libsweep/README.md
Outdated
``` | ||
|
||
Blocks until the `sweep_device_s`'s motor speed has stabilized. This is useful whenever the motor speed is changed. The worst case is a 6 second wait for motor stabilization and calibration. If the motor is already spinning at the desired speed, this returns almost instantly. For visual reference, the blue LED on the device will blink while the motor speed is changing, and stop blinking when the speed has stabilized. If the motor speed has NOT yet stabilized, the device will respond to certain commands (`DS` or `MS`) with a status code indicating a failure to execute the command. Therefore, it is best practice to avoid this entirely by calling `sweep_device_wait_until_motor_ready` before calling a command that requires motor speed is stabilized. | ||
In case of error a `sweep_error_s` will be written into `error`. Will timeout. |
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 "will timeout" mean here? That there is an internal timeout of 6s and it will error out if this limit is exceeded?
libsweep/README.md
Outdated
``` | ||
|
||
Returns 0 if `sweep_device_s`'s motor speed has stabilized. | ||
Returns 1 if not. |
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 require C99+ in the API, so we have stdbool.h
- please use bool here.
https://github.com/scanse/sweep-sdk/blob/master/libsweep/include/sweep/sweep.h#L42
bool sweep_device_is_motor_ready(..)
libsweep/README.md
Outdated
@@ -245,7 +289,16 @@ Opaque type representing a single full 360 degree scan from a `sweep_device_s`. | |||
sweep_scan_s sweep_device_get_scan(sweep_device_s device, sweep_error_s* error) | |||
``` | |||
|
|||
Blocks waiting for the `sweep_device_s` to accumulate a full 360 degree scan into `sweep_scan_s`. | |||
Returns the ordered readings (1st to last) from a single scan. | |||
Retrieves the oldest scan from a queue of scans accumulated in a background thread. Blocks until a scan is available. To be used after calling `sweep_device_start_scanning`, NOT after `sweep_device_attempt_start_scanning`. |
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 and below is what I meant - there now is a direct dependency between functions. And the naming
get_scan vs get_scan_direct
start_scan vs attempt_start_scan
does not really make it clear when to use which version.
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.
Do we give any guarantees wrt. the queue? How large is it? Should the user be able to specify?
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 non low-level development, the API should be unchanged. The "attempt_..." methods are provided as niche case alternatives... and not meant to be advertised. I went ahead and separated these methods out of the main README so that the distinction is clear. See if that makes more sense.
If you have ideas for a better naming convention, please share! I'm not very content with the current "attempt_" and "direct_" pattern.
// create a thread | ||
std::thread th = std::thread(sweep_device_accumulate_scans, device); | ||
// detach the thread so that it runs in the background and cleans itself up | ||
th.detach(); |
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, shouldn't we block (i.e. .join()
) in the device's destructor? That is, set the stop scanning atomic flag, then wait until done.
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.
As I understand it, you cannot join a detached thread. The detached background thread cleans itself up properly when it's method returns or terminates. Seeing how the behavior of the thread exists in the "background" with no need to synchronize, it seems like a much simpler solution to avoid explicitly managing the thread. I could be wrong, but I believe this is the exact kind of intended use for detached threads.
libsweep/src/sweep.cc
Outdated
sweep_error_s readyerror = nullptr; | ||
int32_t motor_ready; | ||
// Only check for 8 seconds (16 iterations with 500ms pause) | ||
for (auto i = 0; i < 16; ++i) { |
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.
Why 16? Why 8 seconds?
libsweep/src/sweep.cc
Outdated
return; | ||
} | ||
// pause for 500ms | ||
std::this_thread::sleep_for(std::chrono::milliseconds(500)); |
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.
Why?
sweeppy/sweeppy/__main__.py
Outdated
sweep.start_scanning() | ||
|
||
# get_scans is coroutine-based generator lazily returning scans ad infinitum | ||
# get_scans is coroutine-based generator lazily returning scans ad | ||
# infinitum |
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.
Why break here?
const uint8_t status_bytes[2] = {response.cmdStatusByte1, response.cmdStatusByte2}; | ||
int32_t status_code = sweep::protocol::ascii_bytes_to_integral(status_bytes); | ||
switch (status_code) { | ||
case 11: |
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 is 11? Can we symbolize this
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.
11 is a failure status code that indicates an invalid parameter. These details are outlined in the protocol docs, but I think it might be best to include them in the protocol implementation. Perhaps making a container of possible status codes for each type of response?
If this is to be released soon, may I suggest to not put the low level functions into the public API just yet? Talking about blocking the merge: For substantial changes like this I often take a triaging approach: Only hold of the merge, until the API is OK and the code appears to be bugfree (that is the tricky part) and then address implementation issues in separate (smaller) PRs. |
@MikeGitb I like the idea of waiting to add these "alternative" methods to the API until they are more fleshed out and the naming convention is settled. The rest of the API should remain unchanged. The implementation differs, and we've added new methods relevant to "motor ready", but the core start/stop methods etc are the same. I'll remove the alternative methods from the API in the next few hours and update the PR. Edit: Updated! |
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.
Minor fixes
libsweep/src/dummy.cc
Outdated
@@ -34,6 +34,65 @@ void sweep_error_destruct(sweep_error_s error) { | |||
delete error; | |||
} | |||
|
|||
// ------------------ Blocking Methods (blocking) ---------------- // |
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.
reorganize the methods to match sweep.h/sweep.cc and remove the outdated comments
sweepjs/README.md
Outdated
@@ -42,10 +42,18 @@ sweep = new Sweep('/dev/ttyUSB0'); | |||
sweep.startScanning(); | |||
sweep.stopScanning(); | |||
|
|||
// waits until motor is ready | |||
sweep.waitUntilMotorReady(); | |||
// code === 0 -> ready (motor ready), code !== 0 -> not ready |
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.
update sweepjs bindings reflect boolean return, instead of a motor ready code
sweeppy/README.md
Outdated
@@ -62,10 +62,12 @@ class Sweep: | |||
def start_scanning(self) -> None | |||
def stop_scanning(self) -> None | |||
|
|||
def get_motor_speed(self) -> int | |||
def wait_until_motor_ready(self) -> None | |||
def get_motor_ready(self) -> int (0 indicates motor is ready) |
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.
indicate that a bool is returned
Just pushed an update to the MAJOR version, and added a table for firmware compatibility in the README. If the appveyor + travis builds look good, I'm going to go ahead and merge this. |
Unfortunately, I realized after merging this that the "wait_until_motor_ready" method was not working properly when being called externally. It works fine internally. This might have something to do with the error handling. Luckily, this method is not actually necessary, given that "start_scanning" and "set_motor_speed" call it internally. Therefore, I stripped the method from the public API and quickly pushed the fix to master. I avoided re-basing master in case anyone already grabbed it. But my apologies if anyone has been working with it during the few hours before the fix. |
This PR brings a few changes:
I have tested the examples, and the bindings on windows. I have tested the examples and bindings on ubuntu 14.04, but @kent-williams mentioned he might have experienced an issue with the python bindings on 16.04. The travis builds might not indicate anything as they're using the dummy build. So i'd love to get additional testing feedback if you have the time @daniel-j-h
This PR needs get merged ASAP, as people will be receiving devices very soon (early/mid next week). I'll go ahead and merge this in a few days if there aren't any complaints. But please add your feedback if you have it!