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

Requirements + Tests for Encoding/Decoding and FC Messages #1

Draft
wants to merge 11 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion .gitmodules
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
[submodule "SI"]
path = SI
path = External/SI
url = https://github.com/teamspatzenhirn/SI.git
[submodule "nanopb"]
path = External/nanopb
url = https://github.com/nanopb/nanopb.git
[submodule "External/MockTools"]
path = External/MockTools
url = https://github.com/ToolboxPlane/MockTools.git
13 changes: 4 additions & 9 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,14 +1,9 @@
project(ToolboxPlaneMessages)

include(FetchContent)
add_subdirectory(External/SI)
add_subdirectory(External/MockTools)

FetchContent_Declare(
nanopb
GIT_REPOSITORY "https://github.com/nanopb/nanopb.git"
GIT_TAG "master"
)
FetchContent_Populate(nanopb)
set(CMAKE_MODULE_PATH ${nanopb_SOURCE_DIR}/extra)
set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/External/nanopb/extra)
find_package(Nanopb REQUIRED)

set(TOOLBOX_PLANE_MESSAGES
Expand All @@ -29,4 +24,4 @@ target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/..
add_library(ToolboxPlaneMessageConversion INTERFACE)
target_link_libraries(ToolboxPlaneMessageConversion INTERFACE ${PROJECT_NAME} SI)

add_subdirectory(SI)
add_subdirectory(Tests)
1 change: 1 addition & 0 deletions External/MockTools
Submodule MockTools added at 0317e2
1 change: 1 addition & 0 deletions External/nanopb
Submodule nanopb added at c16c23
61 changes: 26 additions & 35 deletions MessageDecoding.c
Original file line number Diff line number Diff line change
Expand Up @@ -9,59 +9,50 @@

#include "MessageDefs.h"

void message_decoding_init(message_decoding_data_t *decoding_data, uint8_t message_id) {
decoding_data->decodingState = DECODING_INITIAL;
decoding_data->len = 0;
decoding_data->id = message_id;
void message_decoding_init(message_decoding_data_t *decoding_data, uint8_t message_id, const pb_msgdesc_t *fields) {
decoding_data->_decodingState = DECODING_INITIAL;
decoding_data->_len = 0;
decoding_data->_id = message_id;
decoding_data->_fields = fields;
}

bool message_decoding_decode(message_decoding_data_t *decoding_data, uint8_t data, const pb_msgdesc_t *fields,
void *message) {
switch (decoding_data->decodingState) {
bool message_decoding_decode(message_decoding_data_t *decoding_data, uint8_t data, void *message) {
switch (decoding_data->_decodingState) {
case DECODING_INITIAL:
if (data == END_BYTE) {
decoding_data->decodingState = DECODING_END_FOUND;
decoding_data->_decodingState = DECODING_END_FOUND;
}
break;
case DECODING_END_FOUND:
if (data == START_BYTE) {
decoding_data->decodingState = DECODING_FIRST_FOUND;
if (decoding_data->len > 0) {
decoding_data->len -= 1;

pb_istream_t istream = pb_istream_from_buffer(decoding_data->buf, decoding_data->len);
pb_decode(&istream, fields, message);

decoding_data->len = 0;
return true;
}
decoding_data->_decodingState = DECODING_START_FOUND;
} else {
if (decoding_data->len == 0) {
decoding_data->decodingState = DECODING_INITIAL;
} else {
decoding_data->decodingState = DECODING_IN_DATA;
decoding_data->buf[decoding_data->len] = data;
decoding_data->len += 1;
}
decoding_data->_decodingState = DECODING_INITIAL;
}
break;
case DECODING_FIRST_FOUND:
if (data == decoding_data->id) {
decoding_data->decodingState = DECODING_IN_DATA;
case DECODING_START_FOUND:
if (data == decoding_data->_id) {
decoding_data->_decodingState = DECODING_IN_DATA;
decoding_data->_len = 0;
} else {
decoding_data->decodingState = DECODING_IN_WRONG_DATA;
decoding_data->_decodingState = DECODING_INITIAL;
}
break;
case DECODING_IN_DATA:
if (data == END_BYTE) {
decoding_data->decodingState = DECODING_END_FOUND;
decoding_data->_decodingState = DECODING_IN_DATA_END_FOUND;
}
decoding_data->buf[decoding_data->len] = data;
decoding_data->len += 1;
decoding_data->_buf[decoding_data->_len] = data;
decoding_data->_len += 1;
break;
case DECODING_IN_WRONG_DATA:
if (data == END_BYTE) {
decoding_data->decodingState = DECODING_END_FOUND;
case DECODING_IN_DATA_END_FOUND:
if (data == START_BYTE) {
decoding_data->_decodingState = DECODING_START_FOUND;
pb_istream_t istream = pb_istream_from_buffer(decoding_data->_buf, decoding_data->_len - 1);
pb_decode(&istream, decoding_data->_fields, message);
return true;
} else {
decoding_data->_decodingState = DECODING_IN_DATA;
}
break;
}
Expand Down
93 changes: 78 additions & 15 deletions MessageDecoding.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,45 +24,108 @@
* Internal state of the decoder,
*/
typedef enum {
DECODING_INITIAL, ///< Initial state, no data received
DECODING_END_FOUND, ///< End byte found, waiting for start byte
DECODING_FIRST_FOUND, ///< Start byte received, waiting for ID
DECODING_IN_DATA, ///< Matching ID byte received, waiting for data or end byte
DECODING_IN_WRONG_DATA ///< Other ID byte received, waiting for data or end byte
DECODING_INITIAL, ///< Initial state, no data received
DECODING_END_FOUND, ///< End found, not in package
DECODING_START_FOUND, ///< Start byte received, waiting for ID
DECODING_IN_DATA, ///< Matching ID byte received, waiting for data or end byte
DECODING_IN_DATA_END_FOUND, ///< End byte found, currently in correct package
} message_decoding_state_t;

/**
* All internal data of the decoder.
*/
typedef struct {
uint8_t id; ///< The ID of the message to be received.
message_decoding_state_t decodingState; ///< The state of the decoder
uint8_t len; ///< The length of the received data (excluding start and ID)
uint8_t buf[DECODING_BUF_SIZE]; ///< The buffer for the data
uint8_t _id; ///< The ID of the message to be received.
const pb_msgdesc_t *_fields; ///< Protobuf message description
message_decoding_state_t _decodingState; ///< The state of the decoder
uint8_t _len; ///< The length of the received data (excluding start and ID)
uint8_t _buf[DECODING_BUF_SIZE]; ///< The buffer for the data
} message_decoding_data_t;

#ifdef __cplusplus
extern "C" {
#endif

/**
* Initialize the internal state of the decoder.
* @brief Initialize the internal state of the decoder.
*
* This function performs the following tasks:
* * Set the internal variable "ID" to the argument message_id
* * Set the internal variable "fields" to the argument "fields"
* * Initialize decoding data such that the internal state machine is in the init state
*
* @param decoding_data the state to initialize
* @param message_id id of the message to be received
* @param fields the description of the protobuf-message
*/
void message_decoding_init(message_decoding_data_t *decoding_data, uint8_t message_id);
void message_decoding_init(message_decoding_data_t *decoding_data, uint8_t message_id, const pb_msgdesc_t *fields);

/**
* Decode a message from a datastream.
* @brief Decode a message from a datastream.
*
* The function performs the following tasks:
* * Transition the internal state machine with the input "data" in accordance to the following state machine:
* \dot
* digraph {
* rankdir = "LR";
* INITIAL -> END_FOUND [
* label = "data=0xF0";
* ]
*
* INITIAL -> INITIAL [
* label = "otherwise";
* ]
*
* END_FOUND -> INITIAL [
* label = "otherwise";
* ]
*
* END_FOUND -> START_FOUND [
* label = "data=0x0F";
* ]
*
* IN_DATA_END_FOUND -> IN_DATA [
* label = "otherwise";
* ]
*
* IN_DATA_END_FOUND -> START_FOUND [
* label = "data=0x0F/\nPublish data";
* ]
*
* START_FOUND -> IN_DATA [
* label = "data=ID/\nInit buffer";
* ]
*
* IN_DATA -> IN_DATA [
* label = "otherwise/\nFill buffer";
* ]
*
* IN_DATA -> IN_DATA_END_FOUND [
* label = "data=0xF0/\nFill buffer";
* ]
*
* START_FOUND -> INITIAL [
* label = "otherwise";
* ]
* }
* \enddot
* * with the following actions:
* * **Init buffer**: Clear the internal buffer to contain no items
* * **Fill buffer**: Append "data" to the internal buffer
* * **Publish data**: Performs the following operations:
* * Remove the last (latest) element (which is the end byte) from the buffer
* * Prepare the buffer for protobuf (::pb_istream_from_buffer)
* * Set "message" to the decoded message (::pb_decode)
* * Return true
* * if the **Publish data** was not taken, return false
*
* @param decoding_data the internal state of the decoder
* @param data the new byte used for decoding
* @param fields the description of the protobuf-message
* @param message out-parameter: set to the message if a complete message was received
* @return true if a complete message was received, otherwise false
* @return
*/
bool message_decoding_decode(message_decoding_data_t *decoding_data, uint8_t data, const pb_msgdesc_t *fields,
void *message);
bool message_decoding_decode(message_decoding_data_t *decoding_data, uint8_t data, void *message);

#ifdef __cplusplus
}
Expand Down
5 changes: 3 additions & 2 deletions MessageEncoding.c
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,12 @@

#include "MessageDefs.h"

uint16_t message_encode(uint8_t *buf, uint16_t size, const pb_msgdesc_t *fields, const void *message, uint8_t id) {
uint16_t message_encode(uint8_t *buf, uint16_t size, const pb_msgdesc_t *fields, const void *message,
uint8_t message_id) {
pb_ostream_t ostream = pb_ostream_from_buffer(buf + 2, size - 3);
pb_encode(&ostream, fields, message);
buf[0] = START_BYTE;
buf[1] = id;
buf[1] = message_id;
buf[ostream.bytes_written + 2] = END_BYTE;
return ostream.bytes_written + 3;
}
19 changes: 16 additions & 3 deletions MessageEncoding.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,28 @@ extern "C" {
#endif

/**
* Encode a message
* @brief Encode a message.
*
* This function encodes the data into buf according to the following frame format:
* | Byte | Description | Value |
* | --- | --- | --- |
* | 1 | Start byte | 0x0F |
* | 2 | ID byte | Value of the argument "message_id" |
* | 3..3+N | Protobuf-Payload | Result of pb_encode with the provided fields and message |
* | 4+N | End byte | 0xF0 |
*
* And returns the length of the encoded frame (4+N).
*
* @warning Buffer sizes are not checked
* @param buf the buffer to write to needs to be at least the size of the message + 3
* @param size the size of the buffer
* @param fields the protobuf field description
* @param message a pointer to the message to encode
* @param id the message if of the message
* @param message_id the message if of the message
* @return the size of the encoded message
*/
uint16_t message_encode(uint8_t *buf, uint16_t size, const pb_msgdesc_t *fields, const void *message, uint8_t id);
uint16_t message_encode(uint8_t *buf, uint16_t size, const pb_msgdesc_t *fields, const void *message,
uint8_t message_id);

#ifdef __cplusplus
}
Expand Down
14 changes: 14 additions & 0 deletions Tests/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
add_custom_target(generate_tests
COMMAND ${CMAKE_CURRENT_LIST_DIR}/generate_tests.sh ${CMAKE_SOURCE_DIR}
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
DEPENDS ${CMAKE_CURRENT_LIST_DIR}/generate_tests.sh)

make_test(MODULE_UNDER_TEST ../MessageDecoding.c
TEST_DEFINITION MessageDecoding.cpp)
target_link_libraries(MessageDecoding.test PUBLIC ToolboxPlaneMessageDefs)
add_dependencies(MessageDecoding.test generate_tests)

make_test(MODULE_UNDER_TEST ../MessageEncoding.c
TEST_DEFINITION MessageEncoding.cpp)
target_link_libraries(MessageEncoding.test PUBLIC ToolboxPlaneMessageDefs)
add_dependencies(MessageEncoding.test generate_tests)
Loading