-
Notifications
You must be signed in to change notification settings - Fork 97
Add c api to call user function for every successfully deframed SBP message. [ESD-998] #723
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
Add c api to call user function for every successfully deframed SBP message. [ESD-998] #723
Conversation
|
had a backlog item to add this feature (https://swift-nav.atlassian.net/browse/ESD-998). thanks for picking this up! |
benjaminaltieri
left a comment
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 good. some other ideas we had were creating a SBP_MSG_RETURN_ALL define that you could assign callbacks with in order to achieve the same functionality within current framework but a question was how to forward the raw frames as opposed to the deframed data
c/src/sbp.c
Outdated
| */ | ||
| s8 sbp_process(sbp_state_t *s, s32 (*read)(u8 *buff, u32 n, void *context)) | ||
| { | ||
| return sbp_process_generic(s, read, &sbp_process_payload); |
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.
indent
c/src/sbp.c
Outdated
| } | ||
|
|
||
| s8 sbp_process_generic(sbp_state_t *s, s32 (*read)(u8 *buff, u32 n, void *context), | ||
| s8 (*sbp_process_payload) (sbp_state_t *s, u16 sender_id, u16 msg_type, u8 msg_len, u8 payload[])) |
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.
probably want to avoid aliasing sbp_process_payload here with the arg name. also you might be able to reuse sbp_msg_callback_t if you pass the sbp_state_t as the context * arg
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 renamed the existing sbp_process_payload to sbp_process_payload_cbs to notate that it goes through the linked list of callbacks. This one, I renamed to sbp_process_payload_fn to indicate it is a function pointer.
c/include/libsbp/sbp.h
Outdated
| void sbp_state_init(sbp_state_t *s); | ||
| void sbp_state_set_io_context(sbp_state_t *s, void* context); | ||
| s8 sbp_process(sbp_state_t *s, s32 (*read)(u8 *buff, u32 n, void* context)); | ||
| s8 sbp_process_generic(sbp_state_t *s, s32 (*read)(u8 *buff, u32 n, void *context), |
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.
Nit: This is fairly generic name. Maybe something like sbp_process_msg_catchall or something like that? As an alternative I think a comment explaining what this is meant for would help. I know we don't have any comments for the other functions, but they are sorta-kinda self explanatory while this one isn't.
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 renamed to sbp_process_all and added some comments per your suggestion.
c/include/libsbp/sbp.h
Outdated
| void sbp_state_set_io_context(sbp_state_t *s, void* context); | ||
| s8 sbp_process(sbp_state_t *s, s32 (*read)(u8 *buff, u32 n, void* context)); | ||
| s8 sbp_process_generic(sbp_state_t *s, s32 (*read)(u8 *buff, u32 n, void *context), | ||
| s8 (*sbp_process_payload) (sbp_state_t *s, u16 sender_id, u16 msg_type, u8 msg_len, u8 payload[])); |
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 be hesitant to have the generic callback take in sbp_state_t * directly, the user's code shouldn't be using that. Instead maybe have a void *context as an arg to the generic callback and pass it into sbp_process_generic 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.
Agreed, I think a context was better and more flexible. For the legacy case, the context is the head of the linked list.
c/src/sbp.c
Outdated
|
|
||
| /* Message complete, process it. */ | ||
| s8 ret = sbp_process_payload(s, s->sender_id, s->msg_type, s->msg_len, | ||
| s8 ret = (*sbp_process_payload)(s, s->sender_id, s->msg_type, s->msg_len, |
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 you give this arg a different name then you shouldn't need to deference the function pointer here.
ec1f6b7 to
5f40085
Compare
jbangelo
left a comment
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.
LGTM! If/when we make a v2.0 of libsbp I think one of the alternative approaches we discussed would be preferable, but API stability is more important right now I think.
c/include/libsbp/sbp.h
Outdated
| s8 (*sbp_process_payload) (u16 sender_id, u16 msg_type, u8 msg_len, | ||
| u8 payload[], void* context), | ||
| void* process_payload_context); | ||
| s8 sbp_process_payload_cbs(u16 sender_id, u16 msg_type, u8 msg_len, |
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'm waffling on if sbp_process_payload_cbs() should be publicly declared in the header or not. On the one hand I don't think we would need to expose this to the consumer in most cases. But there is at least the case where the consumer might want to dynamically switch between using the registered callbacks and just getting everrything, in which case providing sbp_process_payload_cbs() is helpful...
I guess with how libsbp is currently structured it makes sense making this public. Ideally consumers wouldn't monkey around with the inner values of the libsbp structs, but they are already exposed and exposing this limits the amount of internal stuff they would need to deal with in the case I described.
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.
Couldn't this be reduced to one (or two) API additions and no API changes? That is
sbp_set_payload_cb(sbp_state_t* s, sbp_process_all_fn_t fn)
?
c/include/libsbp/sbp.h
Outdated
| * deframed sbp message. This is useful for logging or forwarding any message | ||
| * regardless of type. Optionally, it can call sbp_process_payload_cbs itself | ||
| * to invoke matching callbacks in the linked list as well.*/ | ||
| s8 sbp_process_all(sbp_state_t *s, s32 (*read)(u8 *buff, u32 n, void *context), |
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 definitely prefer this naming scheme, but I agree with @silverjam that we shouldn't change the public API excepting for additions (just like we have to do with sbp messages)
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 need be we can marshall sbp_process_payload directly to sbp_process_payload_cbs with a comment stating that this api is deprecated.
6df0bc6 to
cba446b
Compare
|
I've updated one more time in such a way that there is no change to existing API. I've also added unit tests. |
benjaminaltieri
left a comment
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.
definitely closer to what was originally proposed for that backlog ticket. a couple ideas to integrate but looking good otherwise
c/src/sbp.c
Outdated
| @@ -1,4 +1,4 @@ | |||
| /* | |||
| /* | |||
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.
missing asterisk and incorrect indentation
c/src/sbp.c
Outdated
| * \return `SBP_OK` (0) if successful, `SBP_NULL_ERROR` if fn is NULL | ||
| * | ||
| */ | ||
| s8 sbp_register_allmsg_callback(sbp_state_t *s, sbp_allmsg_callback_fn_t fn, |
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 believe this is a 'set' operation, register implies multiple such callbacks can be registered which isn't true in this case
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 is good point; I will rename to "sbp_set_allmsg_callback". Do you like the "allmsg" wording?
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.
Now I am thinking to call it sbp_set_allmsg_fn() or something similar as to semantically differentiate between this feature, and the linked list of callbacks for particular message types.
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.
agree I renamed
c/src/sbp.c
Outdated
| * Register a callback that is called when any | ||
| * valid message is received. | ||
| * | ||
| * \param msg_type Message type associated with callback |
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 should be s SBP state context
| s8 sbp_register_allmsg_callback(sbp_state_t *s, sbp_allmsg_callback_fn_t fn, | ||
| void *context) { | ||
| /* Check our callback function pointer isn't NULL. */ | ||
| if (0 == fn) { |
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.
NULL preferred for pointer checks
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 agree, but in order to do this it requires adding a new #include that wasn't here when the original author wrote. I thought perhaps there was a portability reason to not include stddefs. I'm happy to follow up with a separate PR to add in NULL for null pointer and the new include, but for now I wanted to follow the existing pattern.
c/src/sbp.c
Outdated
| * \return `SBP_OK` (0) if successful | ||
| */ | ||
| s8 sbp_remove_allmsg_callback(sbp_state_t *s) { | ||
| s->allmsg_cb = 0; |
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.
NULL preferred
c/src/sbp.c
Outdated
| /* Reset the head of the callbacks list to NULL. */ | ||
| s->sbp_msg_callbacks_head = 0; | ||
| /* Reset the all_cb to 0. */ | ||
| s->allmsg_cb = 0; |
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 argue this shouldn't be included, sbp_remove_allmsg_callback already covers clearing 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.
I was debating that as well. If I don't use the word "callback" for the "allmsg_fuction", then this would be very clear and I wouldn't need to change sbp_clear_callbacks.
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.
after renaming I agree and changed.
c/src/sbp.c
Outdated
| ret = SBP_OK_CALLBACK_EXECUTED; | ||
| } | ||
| } | ||
| if (0 != s->allmsg_cb) { |
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.
use NULL
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 discussed, separate PR to be up today
c/src/sbp.c
Outdated
| } | ||
| } | ||
| if (0 != s->allmsg_cb) { | ||
| (*s->allmsg_cb)(sender_id, msg_type, msg_len, payload, s->allmsg_context); |
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.
| (*s->allmsg_cb)(sender_id, msg_type, msg_len, payload, s->allmsg_context); | |
| *(s->allmsg_cb)(sender_id, msg_type, msg_len, payload, s->allmsg_context); |
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.
or omit deref, it isn't technically necessary
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.
done.
9252ddf to
097b116
Compare
| #define SBP_MINOR_VERSION 6 | ||
| /** Protocol patch version. */ | ||
| #define SBP_PATCH_VERSION 4 | ||
| #define SBP_PATCH_VERSION 6 |
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 need to touch this, it's bumped automatically during a release.
| s8 sbp_register_callback(sbp_state_t* s, u16 msg_type, sbp_msg_callback_t cb, void* context, | ||
| sbp_msg_callbacks_node_t *node); | ||
| /* sets a function to be called on ANY deframed message */ | ||
| s8 sbp_set_allmsg_fn(sbp_state_t *s, sbp_allmsg_fn_t fn, void *context); |
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.
How about sbp_register_allmsg_callback (or sbp_register_all_msg_callback) to match sbp_register_callback?
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 believe I suggested set since this is not registered, as in place in a registry of callbacks along with other callbacks. there is only one allmsg function to either set or clear
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 was worried about calling it a "callback". If I do, then I think it makes sense that sbp_clear_callbacks clears this allmsg callback as well, which is kind of a change in behavior for that part of the API that Ben wasn't too fond of.
| u8 msg_buff[256]; | ||
| void* io_context; | ||
| sbp_msg_callbacks_node_t* sbp_msg_callbacks_head; | ||
| sbp_allmsg_fn_t allmsg_fn; |
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.
No a huge fall of allmsg, would prefer all_msg, but I can live with it.
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 just didn't want anyone to be confused that all_msg meant all messages.
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.
agree on this point, considering one of the use cases of this function (hence the different signature) is to receive the entire raw frame so the binary blob can be written to file. from this perspective I would almost prefer a name like sbp_raw_frame_handler or something to that effect
| * \return `SBP_OK` (0) if successful | ||
| */ | ||
| s8 sbp_remove_allmsg_fn(sbp_state_t *s) { | ||
| s->allmsg_fn = 0; |
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.
Clear allmsg_context too?
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.
yes I agree.
silverjam
left a comment
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.
Looking good, a few minor nits from me though.
097b116 to
03ecb92
Compare
03ecb92 to
7ca15c7
Compare
|
Superceded by #725 |
This allows a user to use sbp_process_generic and call the user function for every sbp message. It doesn't change our API at all and could simplify many things like logging SBP messages to a file without needing to copy over the sbp deframing code.
ESD-998