-
Notifications
You must be signed in to change notification settings - Fork 97
Frame callback implementation & tests #725
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
Conversation
1c91afd to
63eb051
Compare
eb99016 to
73ed3e4
Compare
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 great so far, really like the registration updates. lots of idea to explore here, I'm mostly looking to reduce the doubling up I'm seeing between frame and message buffers during sbp_process and the double list traversal going on in sbp_process_payload/sbp_process_frame
c/include/libsbp/sbp.h
Outdated
| u16 frame_len; | ||
| u8 n_read; | ||
| u8 msg_buff[256]; | ||
| u8 frame_buff[256 + 8]; |
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.
can we define SBP_MAX_FRAME_LEN with this value and use it? also, the above line could use SBP_MAX_PAYLOAD_LEN
c/include/libsbp/sbp.h
Outdated
| * This enum is stored on each sbp_msg_callback_node struct to identify how | ||
| * to cast the callback function pointer stored within. | ||
| */ | ||
| enum sbp_cb_type{ |
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 space
c/src/sbp.c
Outdated
| if (s->msg_len - s->n_read <= 0) { | ||
| s->n_read = 0; | ||
| s->state = GET_CRC; | ||
| for (int i = 0; i < s->msg_len; 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.
looking at the code now, it seems as though msg_buff and frame_buff are redundant. reading directly into msg_type, sender_id, etc. seems like an optimization used since we weren't trying to record the whole frame. since it's only 8 extra bytes and we could just as easily extract those values from frame_buff at the appropriate time, I would propose simply removing msg_buff altogether and returning the usual msg[] pointer as an offset from the beginning of frame_buff
c/src/sbp.c
Outdated
| s->state = GET_MSG; | ||
| s->frame_buff[5] = s->msg_len; | ||
| /* length of frame is msg_len + header_len + 2 for CRC */ | ||
| s->frame_len = s->msg_len + SBP_HEADER_LEN + SBP_CRC_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.
frame_len should be incremented on each read (assuming implementation of previous comment) and validated against this formula. this will allow early exits to reflect the actual bytes held in frame_buff when we add frame error handlers
c/src/sbp.c
Outdated
| * In future, it may be wise to add new return codes for the truth | ||
| * table of both process function return codes. For now, we return only | ||
| * sbp_process_payload's return code for backwards compatibility. */ | ||
| sbp_process_frame(s, s->sender_id, s->msg_type, s->msg_len, s->msg_buff, |
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 propose we allow sbp_process_frame to take an argument such that it calls all or a subset of callback types. this would allow sbp_process_payload to be a specialized call to sbp_process_frame where only SBP_TYPE_CALLBACK nodes are executed (similar to how you are extending the callback register functions
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 would reduce code duplication between the two methods as well as avoid having to traverse the list twice.
| void logging_reset(void) | ||
| { | ||
| n_callbacks_logged = 0; | ||
| n_frame_callbacks_logged = 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.
should the last_* variables get reset 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, that would be better. updated.
| tcase_add_test(tc_core, test_callbacks); | ||
| tcase_add_test(tc_core, test_sbp_send_message); | ||
| tcase_add_test(tc_core, test_sbp_process); | ||
| tcase_add_test(tc_core, test_sbp_frame); |
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.
adding tests too! this is great, much good karma to you 🕉
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.
❤️ ❤️ ❤️ ❤️ ❤️ ❤️
c/include/libsbp/sbp.h
Outdated
| */ | ||
| enum sbp_cb_type{ | ||
| SBP_TYPE_CALLBACK, | ||
| SBP_FRAME_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.
in true C style, we could turn this into a bit field to allow the sbp_process_frame function to be super flexible, just throw in a bitmask to select the callback styles you'd like to service. as easy as:
if ((node->cb_type & callback_select_mask) &&
((node->msg_type == msg_type) || (node->msg_type == SBP_MSG_ALL))) {
c/src/sbp.c
Outdated
| s->n_read = 0; | ||
| /* set frame_buff to 0 after each preamble. | ||
| * Note, library functions are not used to avoid more dependencies. */ | ||
| for (int i = 0; i < 255; 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.
255 doesn't seem right here. since it's a static buffer we can use sizeof(s->frame_buff)
|
Put these changes together to help illustrate the ideas: https://github.com/swift-nav/libsbp/tree/benaltieri/frame_callback_ideas |
|
Ready for review. |
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.
Mostly doc suggestions and pontificating about big vs little endian. Looking much better though.
c/src/sbp.c
Outdated
| if (expected_frame_len != s->frame_len) { | ||
| return SBP_READ_TOO_MANY_ERROR; | ||
| } | ||
| /* Swap bytes to little endian. */ |
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.
TL;DR Leaving the rest of this here for posterity, but I think we should remove these comments, since it's not correct. To fully handle endianness issues we need to rewrite some of the generated code to handle it too, so not in the purview of this PR.
---8<---
Is this comment correct? The value is stored in little endian (if the machine it's generated on is little endian), we're not really swapping anything.
Using *((u16) &s->frame_buff[SBP_FRAME_OFFSET_CRC(s->msg_len)]) will work the same. E.g. https://godbolt.org/z/MZ1MCX -- Not saying this shouldn't be here, but we need to do the byte swap when storing the value on big endian for this to make sense.
That is, when we build an SBP packet, we should have something like:
#define BUF_TO_U16(buf, offset) *((u16*)&buf[0])
#ifdef __BIG_ENDIAN__
#define HTON16(crc) ((crc << 8) | crc)
#else // __LITTLE_ENDIAN__
#define HTON16(crc) (crc)
#endifThen when generating the CRC for an outgoging SBP packet:
s->crc = HTON16(crc);(Note our "network" byte order is little endian, which is opposite of the usual meaning of "network byte order").
--->8---
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.
Per this good feedback I removed the bogus comment and created a helper function to cast to u8s to a u16. I would love to generate real parsing functions that are platform independent for each field for SBP with the generator and hope to accomplish that.
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.
Big endian seems pretty uncommon lately, so I think it's a "nice to have", we have definitely seen (1 or 2) customers with this issue, but it shouldn't be high priority.
| tcase_add_test(tc_core, test_callbacks); | ||
| tcase_add_test(tc_core, test_sbp_send_message); | ||
| tcase_add_test(tc_core, test_sbp_process); | ||
| tcase_add_test(tc_core, test_sbp_frame); |
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.
❤️ ❤️ ❤️ ❤️ ❤️ ❤️
467ff4a to
317a1ca
Compare
c/include/libsbp/sbp.h
Outdated
| * to cast the callback function pointer stored within. | ||
| */ | ||
| enum sbp_cb_type { | ||
| SBP_TYPE_CALLBACK = 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 am starting to think this should be SBP_PAYLOAD_CALLBACK instead of SBP_TYPE_CALLBACK. I had named it type callback when it was impossible to register a frame callback on a particular message type.
|
I've made a few changes. I added a helper function to go from u8[] to u16 and remove confusing endianness comments, renamed SBP_TYPE_CALLBACK to SBP_PAYLOAD_CB, and updated the docs a lot per Jason's suggestions and thoughts. If approved I will plan to rebase and merge as is with the relatively fine-grained commit history. If requested, I am willing and able to squash into three logical commits.
|
|
@denniszollo I think some squashing is in order, whatever seems appropriate to you is fine. |
99dea33 to
0d13602
Compare
|
After working with this new API quite a bit on Friday, it became clear to me that the new return code (SBP_READ_TOO_MANY_ERROR) could result in a change in behavior depending on how the *read function pointer was implemented, as such Ben and I decided to remove it. I am going to merge the newly squashed version of this with only this one change. |
|
@denniszollo squashing helped, my only nit would be that the macro commit should be before the commit that uses the macros implemented there. one example is |
* classify callbacks into SBP_FRAME_CALLBACK(new) and SBP_PAYLOAD_CALLBACK (original)
* add API for an ALL message cb to c lib
* add macro for frame length calculation
* c_frame_callback: add sbp_u8_array_to_u16 for sbp_process.
* c_frame_callback: Add unit tests for all_msg callback api * c_frame_callback: Add unit test for max length (255) to c lib
0d13602 to
7b9ac44
Compare


This PR adds a new type of callback to the c SBP Library to handle the entire SBP frame. It leaves the external API unchanged except for these additions.
This new frame callback feature will allow for easier raw logging in applications including Piksi Multi's standalone logger. It also allows adding a frame callback of type SBP_MSG_ALL to fire a callback for any msg_type. A convenient helper function is added to the API for registering a callback for every msg_type. Implementation details were discussed internally quite a bit and I think this represents the consensus.
I've added unit tests for the new functionality and some missing unit tests like testing the max_size of SBP messages.