Skip to content

Conversation

@denniszollo
Copy link
Contributor

@denniszollo denniszollo commented Sep 3, 2019

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

@benjaminaltieri benjaminaltieri changed the title Add c api to call user function for every successfully deframed SBP message. Add c api to call user function for every successfully deframed SBP message. [ESD-998] Sep 3, 2019
@benjaminaltieri
Copy link
Contributor

had a backlog item to add this feature (https://swift-nav.atlassian.net/browse/ESD-998). thanks for picking this up!

Copy link
Contributor

@benjaminaltieri benjaminaltieri left a 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);
Copy link
Contributor

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[]))
Copy link
Contributor

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

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 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.

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),
Copy link
Contributor

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.

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 renamed to sbp_process_all and added some comments per your suggestion.

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[]));
Copy link
Contributor

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?

Copy link
Contributor Author

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,
Copy link
Contributor

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.

@denniszollo denniszollo force-pushed the sbp_process_so_callbacks_c branch 5 times, most recently from ec1f6b7 to 5f40085 Compare September 3, 2019 21:48
Copy link
Contributor

@jbangelo jbangelo left a 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.

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,
Copy link
Contributor

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.

Copy link
Contributor

@silverjam silverjam left a 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)

?

* 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),
Copy link
Contributor

@benjaminaltieri benjaminaltieri Sep 4, 2019

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)

Copy link
Contributor

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.

@denniszollo denniszollo force-pushed the sbp_process_so_callbacks_c branch 5 times, most recently from 6df0bc6 to cba446b Compare September 5, 2019 05:49
@denniszollo
Copy link
Contributor Author

I've updated one more time in such a way that there is no change to existing API. I've also added unit tests.

Copy link
Contributor

@benjaminaltieri benjaminaltieri left a 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 @@
/*
/*
Copy link
Contributor

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,
Copy link
Contributor

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

Copy link
Contributor Author

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?

Copy link
Contributor Author

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.

Copy link
Contributor Author

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
Copy link
Contributor

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) {
Copy link
Contributor

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

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 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;
Copy link
Contributor

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;
Copy link
Contributor

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

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 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.

Copy link
Contributor Author

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) {
Copy link
Contributor

Choose a reason for hiding this comment

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

use NULL

Copy link
Contributor Author

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);
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
(*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);

Copy link
Contributor

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

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done.

@denniszollo denniszollo force-pushed the sbp_process_so_callbacks_c branch 2 times, most recently from 9252ddf to 097b116 Compare September 5, 2019 19:15
#define SBP_MINOR_VERSION 6
/** Protocol patch version. */
#define SBP_PATCH_VERSION 4
#define SBP_PATCH_VERSION 6
Copy link
Contributor

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);
Copy link
Contributor

@silverjam silverjam Sep 5, 2019

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?

Copy link
Contributor

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

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 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;
Copy link
Contributor

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.

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 just didn't want anyone to be confused that all_msg meant all messages.

Copy link
Contributor

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;
Copy link
Contributor

Choose a reason for hiding this comment

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

Clear allmsg_context too?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

yes I agree.

Copy link
Contributor

@silverjam silverjam left a 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.

@denniszollo denniszollo force-pushed the sbp_process_so_callbacks_c branch from 097b116 to 03ecb92 Compare September 5, 2019 19:38
@denniszollo denniszollo force-pushed the sbp_process_so_callbacks_c branch from 03ecb92 to 7ca15c7 Compare September 5, 2019 19:47
@denniszollo
Copy link
Contributor Author

Superceded by #725

@denniszollo denniszollo closed this Sep 6, 2019
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.

4 participants