diff --git a/.gitmodules b/.gitmodules index 0ffe77a..f76101b 100644 --- a/.gitmodules +++ b/.gitmodules @@ -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 diff --git a/CMakeLists.txt b/CMakeLists.txt index 7180cea..ca8c111 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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 @@ -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) diff --git a/External/MockTools b/External/MockTools new file mode 160000 index 0000000..0317e24 --- /dev/null +++ b/External/MockTools @@ -0,0 +1 @@ +Subproject commit 0317e2490f8a557d418aa4c6c2fce43f29e1e377 diff --git a/SI b/External/SI similarity index 100% rename from SI rename to External/SI diff --git a/External/nanopb b/External/nanopb new file mode 160000 index 0000000..c16c239 --- /dev/null +++ b/External/nanopb @@ -0,0 +1 @@ +Subproject commit c16c2399d1d1ba1e4a8d0f288cf9c158fdc85a0d diff --git a/MessageDecoding.c b/MessageDecoding.c index d728f98..146ee00 100644 --- a/MessageDecoding.c +++ b/MessageDecoding.c @@ -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; } diff --git a/MessageDecoding.h b/MessageDecoding.h index 284c595..cf9385c 100644 --- a/MessageDecoding.h +++ b/MessageDecoding.h @@ -24,21 +24,22 @@ * 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 @@ -46,23 +47,85 @@ 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 } diff --git a/MessageEncoding.c b/MessageEncoding.c index d959e1f..2f2db48 100644 --- a/MessageEncoding.c +++ b/MessageEncoding.c @@ -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; } diff --git a/MessageEncoding.h b/MessageEncoding.h index c311bce..9fe5919 100644 --- a/MessageEncoding.h +++ b/MessageEncoding.h @@ -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 } diff --git a/Tests/CMakeLists.txt b/Tests/CMakeLists.txt new file mode 100644 index 0000000..750ce1f --- /dev/null +++ b/Tests/CMakeLists.txt @@ -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) diff --git a/Tests/MessageDecoding.cpp b/Tests/MessageDecoding.cpp new file mode 100644 index 0000000..1dd07b9 --- /dev/null +++ b/Tests/MessageDecoding.cpp @@ -0,0 +1,436 @@ +#include +#include + +extern "C" { +#include +} + +#include "FlightControllerSetpoint.pb.h" + +TEST(TEST_NAME, decode__nominal_decode) { + /* + * Nominal decode test. + * Test: + * * Initialize with message_id as 17 + * * Feed 0xF0 to transition to END_FOUND, expect false + * * Feed 0x0F to transition to START_FOUND, expect false + * * Feed 17 to transition to IN_DATA, expect false + * * Feed 0xF0 to transition to IN_DATA_END_FOUND, expect false + * * Feed 0x0F to transition to START_FOUND, expect true + */ + message_decoding_data_t decodingData; + message_decoding_init(&decodingData, 17, ToolboxPlaneMessages_FlightControllerSetpoint_fields); + + EXPECT_FALSE(message_decoding_decode(&decodingData, 0xF0, nullptr)); + EXPECT_FALSE(message_decoding_decode(&decodingData, 0x0F, nullptr)); + EXPECT_FALSE(message_decoding_decode(&decodingData, 17, nullptr)); + EXPECT_FALSE(message_decoding_decode(&decodingData, 0xF0, nullptr)); + ToolboxPlaneMessages_FlightControllerSetpoint buf; + EXPECT_TRUE(message_decoding_decode(&decodingData, 0x0F, &buf)); +} + +TEST(TEST_NAME, decode__premature_end) { + /* + * Premature end test. + * Test: + * * Initialize with message_id as 17 + * * Feed 0xF0 to transition to END_FOUND, expect false + * * Feed 0x0F to transition to START_FOUND, expect false + * * Feed 17 to transition to IN_DATA, expect false + * * Feed 0x08 to keep in IN_DATA, expect false + * * Feed 0xF0 to transition to IN_DATA_END_FOUND, expect false + * * Feed 0x01 to transition to IN_DATA, expect false + * * Feed 0xF0 to transition to IN_DATA_END_FOUND, expect false + * * Feed 0x0F to transition to START_FOUND, expect true + */ + message_decoding_data_t decodingData; + message_decoding_init(&decodingData, 17, ToolboxPlaneMessages_FlightControllerSetpoint_fields); + + EXPECT_FALSE(message_decoding_decode(&decodingData, 0xF0, nullptr)); + EXPECT_FALSE(message_decoding_decode(&decodingData, 0x0F, nullptr)); + EXPECT_FALSE(message_decoding_decode(&decodingData, 17, nullptr)); + EXPECT_FALSE(message_decoding_decode(&decodingData, 0x08, nullptr)); + EXPECT_FALSE(message_decoding_decode(&decodingData, 0xF0, nullptr)); + EXPECT_FALSE(message_decoding_decode(&decodingData, 0x01, nullptr)); + EXPECT_FALSE(message_decoding_decode(&decodingData, 0xF0, nullptr)); + ToolboxPlaneMessages_FlightControllerSetpoint buf; + EXPECT_TRUE(message_decoding_decode(&decodingData, 0x0F, &buf)); +} + +TEST(TEST_NAME, decode__wrong_data) { + /* + * Wrong data test. + * Test: + * * Initialize with message_id as 17 + * * Feed 0xF0 to transition to END_FOUND, expect false + * * Feed 0x0F to transition to START_FOUND, expect false + * * Feed 18 to transition to INITIAL, expect false + * * Feed 0xF0 to transition to END_FOUND, expect false + * * Feed 0x0F to transition to START_FOUND, expect false + * * Feed 17 to transition to IN_DATA, expect false + * * Feed 0xF0 to transition to IN_DATA_END_FOUND, expect false + * * Feed 0x0F to transition to START_FOUND, expect true + */ + message_decoding_data_t decodingData; + message_decoding_init(&decodingData, 17, ToolboxPlaneMessages_FlightControllerSetpoint_fields); + + EXPECT_FALSE(message_decoding_decode(&decodingData, 0xF0, nullptr)); + EXPECT_FALSE(message_decoding_decode(&decodingData, 0x0F, nullptr)); + EXPECT_FALSE(message_decoding_decode(&decodingData, 18, nullptr)); + EXPECT_FALSE(message_decoding_decode(&decodingData, 0xF0, nullptr)); + EXPECT_FALSE(message_decoding_decode(&decodingData, 0x0F, nullptr)); + EXPECT_FALSE(message_decoding_decode(&decodingData, 17, nullptr)); + EXPECT_FALSE(message_decoding_decode(&decodingData, 0xF0, nullptr)); + ToolboxPlaneMessages_FlightControllerSetpoint buf; + EXPECT_TRUE(message_decoding_decode(&decodingData, 0x0F, &buf)); +} + +TEST(TEST_NAME, decode__wrong_data_premature_end) { + /* + * Wrong Data Premature end test. + * Test: + * * Initialize with message_id as 17 + * * Feed 0xF0 to transition to END_FOUND, expect false + * * Feed 0x0F to transition to START_FOUND, expect false + * * Feed 18 to transition to INITIAL, expect false + * * Feed 0x08 to keep in INITIAL, expect false + * * Feed 0xF0 to transition to END_FOUND, expect false + * * Feed 0x01 to transition to IN_DATA, expect false + * * Feed 0xF0 to transition to IN_DATA_END_FOUND, expect false + * * Feed 0x0F to transition to START_FOUND, expect false + * * Feed 17 to transition to IN_DATA, expect false + * * Feed 0xF0 to transition to IN_DATA_END_FOUND, expect false + * * Feed 0x0F to transition to START_FOUND, expect true + */ + message_decoding_data_t decodingData; + message_decoding_init(&decodingData, 17, ToolboxPlaneMessages_FlightControllerSetpoint_fields); + + EXPECT_FALSE(message_decoding_decode(&decodingData, 0xF0, nullptr)); + EXPECT_FALSE(message_decoding_decode(&decodingData, 0x0F, nullptr)); + EXPECT_FALSE(message_decoding_decode(&decodingData, 18, nullptr)); + EXPECT_FALSE(message_decoding_decode(&decodingData, 0x08, nullptr)); + EXPECT_FALSE(message_decoding_decode(&decodingData, 0xF0, nullptr)); + EXPECT_FALSE(message_decoding_decode(&decodingData, 0x01, nullptr)); + EXPECT_FALSE(message_decoding_decode(&decodingData, 0xF0, nullptr)); + EXPECT_FALSE(message_decoding_decode(&decodingData, 0x0F, nullptr)); + EXPECT_FALSE(message_decoding_decode(&decodingData, 17, nullptr)); + EXPECT_FALSE(message_decoding_decode(&decodingData, 0xF0, nullptr)); + ToolboxPlaneMessages_FlightControllerSetpoint buf; + EXPECT_TRUE(message_decoding_decode(&decodingData, 0x0F, &buf)); +} + +TEST(TEST_NAME, decode__multiple_init) { + /* + * Multiple init decode test. + * Test: + * * Initialize with message_id as 17 + * * Feed 0x00 to keep in initial + * * Feed 0xF0 to transition to END_FOUND, expect false + * * Feed 0x0F to transition to START_FOUND, expect false + * * Feed 17 to transition to IN_DATA, expect false + * * Feed 0xF0 to transition to IN_DATA_END_FOUND, expect false + * * Feed 0x0F to transition to START_FOUND, expect true + */ + message_decoding_data_t decodingData; + message_decoding_init(&decodingData, 17, ToolboxPlaneMessages_FlightControllerSetpoint_fields); + + EXPECT_FALSE(message_decoding_decode(&decodingData, 0x00, nullptr)); + EXPECT_FALSE(message_decoding_decode(&decodingData, 0xF0, nullptr)); + EXPECT_FALSE(message_decoding_decode(&decodingData, 0x0F, nullptr)); + EXPECT_FALSE(message_decoding_decode(&decodingData, 17, nullptr)); + EXPECT_FALSE(message_decoding_decode(&decodingData, 0xF0, nullptr)); + ToolboxPlaneMessages_FlightControllerSetpoint buf; + EXPECT_TRUE(message_decoding_decode(&decodingData, 0x0F, &buf)); +} + +TEST(TEST_NAME, decode__init_wrong_end) { + /* + * Init wrong end test. + * Test: + * * Initialize with message_id as 17 + * * Feed 0xF0 to transition to END_FOUND, expect false + * * Feed 0x00 to transition to INITIAL, expect false + * * Feed 0xF0 to transition to END_FOUND, expect false + * * Feed 0x0F to transition to START_FOUND, expect false + * * Feed 17 to transition to IN_DATA, expect false + * * Feed 0xF0 to transition to IN_DATA_END_FOUND, expect false + * * Feed 0x0F to transition to START_FOUND, expect true + */ + message_decoding_data_t decodingData; + message_decoding_init(&decodingData, 17, ToolboxPlaneMessages_FlightControllerSetpoint_fields); + + EXPECT_FALSE(message_decoding_decode(&decodingData, 0xF0, nullptr)); + EXPECT_FALSE(message_decoding_decode(&decodingData, 0x00, nullptr)); + EXPECT_FALSE(message_decoding_decode(&decodingData, 0xF0, nullptr)); + EXPECT_FALSE(message_decoding_decode(&decodingData, 0x0F, nullptr)); + EXPECT_FALSE(message_decoding_decode(&decodingData, 17, nullptr)); + EXPECT_FALSE(message_decoding_decode(&decodingData, 0xF0, nullptr)); + ToolboxPlaneMessages_FlightControllerSetpoint buf; + EXPECT_TRUE(message_decoding_decode(&decodingData, 0x0F, &buf)); +} + +TEST(TEST_NAME, decode__fcs_decode) { + /* + * Flight-Controller-Set-Point min power test: + * * Initialize with message_id as 0 + * * Feed 0xF0 to transition to END_FOUND, expect false + * * Feed 0x0F to transition to START_FOUND, expect false + * * Feed 0 to transition to IN_DATA, expect false + * * Feed data from fcs_min_power.bin, expect false for every byte + * * Feed 0xF0 to transition to IN_DATA_END_FOUND, expect false + * * Feed 0x0F to transition to START_FOUND, expect true + * * Check return message, expect: + * * motor: -2147483648 + * * pitch: 0 + * * roll: 0 + */ + message_decoding_data_t decodingData; + message_decoding_init(&decodingData, 0, ToolboxPlaneMessages_FlightControllerSetpoint_fields); + + EXPECT_FALSE(message_decoding_decode(&decodingData, 0xF0, nullptr)); + EXPECT_FALSE(message_decoding_decode(&decodingData, 0x0F, nullptr)); + EXPECT_FALSE(message_decoding_decode(&decodingData, 0, nullptr)); + + std::ifstream stream{"fcs_decode.bin"}; + using c_stream_it = std::istreambuf_iterator; + + for (auto it = c_stream_it{stream}; it != c_stream_it{}; ++it) { + EXPECT_FALSE(message_decoding_decode(&decodingData, *it, nullptr)); + } + + EXPECT_FALSE(message_decoding_decode(&decodingData, 0xF0, nullptr)); + ToolboxPlaneMessages_FlightControllerSetpoint buf; + EXPECT_TRUE(message_decoding_decode(&decodingData, 0x0F, &buf)); + + EXPECT_EQ(buf.motor, 17); + EXPECT_EQ(buf.pitch, -18); + EXPECT_EQ(buf.roll, 19); +} + +TEST(TEST_NAME, decode__fcs_min_power) { + /* + * Flight-Controller-Set-Point min power test: + * * Initialize with message_id as 0 + * * Feed 0xF0 to transition to END_FOUND, expect false + * * Feed 0x0F to transition to START_FOUND, expect false + * * Feed 0 to transition to IN_DATA, expect false + * * Feed data from fcs_min_power.bin, expect false for every byte + * * Feed 0xF0 to transition to IN_DATA_END_FOUND, expect false + * * Feed 0x0F to transition to START_FOUND, expect true + * * Check return message, expect: + * * motor: -2147483648 + * * pitch: 0 + * * roll: 0 + */ + message_decoding_data_t decodingData; + message_decoding_init(&decodingData, 0, ToolboxPlaneMessages_FlightControllerSetpoint_fields); + + EXPECT_FALSE(message_decoding_decode(&decodingData, 0xF0, nullptr)); + EXPECT_FALSE(message_decoding_decode(&decodingData, 0x0F, nullptr)); + EXPECT_FALSE(message_decoding_decode(&decodingData, 0, nullptr)); + + std::ifstream stream{"fcs_min_power.bin"}; + using c_stream_it = std::istreambuf_iterator; + + for (auto it = c_stream_it{stream}; it != c_stream_it{}; ++it) { + EXPECT_FALSE(message_decoding_decode(&decodingData, *it, nullptr)); + } + + EXPECT_FALSE(message_decoding_decode(&decodingData, 0xF0, nullptr)); + ToolboxPlaneMessages_FlightControllerSetpoint buf; + EXPECT_TRUE(message_decoding_decode(&decodingData, 0x0F, &buf)); + + EXPECT_EQ(buf.motor, -2147483648); + EXPECT_EQ(buf.pitch, 0); + EXPECT_EQ(buf.roll, 0); +} + +TEST(TEST_NAME, decode__fcs_max_power) { + /* + * Flight-Controller-Set-Point min power test: + * * Initialize with message_id as 0 + * * Feed 0xF0 to transition to END_FOUND, expect false + * * Feed 0x0F to transition to START_FOUND, expect false + * * Feed 0 to transition to IN_DATA, expect false + * * Feed data from fcs_max_power.bin, expect false for every byte + * * Feed 0xF0 to transition to IN_DATA_END_FOUND, expect false + * * Feed 0x0F to transition to START_FOUND, expect true + * * Check return message, expect: + * * motor: 2147483647 + * * pitch: 0 + * * roll: 0 + */ + message_decoding_data_t decodingData; + message_decoding_init(&decodingData, 0, ToolboxPlaneMessages_FlightControllerSetpoint_fields); + + EXPECT_FALSE(message_decoding_decode(&decodingData, 0xF0, nullptr)); + EXPECT_FALSE(message_decoding_decode(&decodingData, 0x0F, nullptr)); + EXPECT_FALSE(message_decoding_decode(&decodingData, 0, nullptr)); + + std::ifstream stream{"fcs_max_power.bin"}; + using c_stream_it = std::istreambuf_iterator; + + for (auto it = c_stream_it{stream}; it != c_stream_it{}; ++it) { + EXPECT_FALSE(message_decoding_decode(&decodingData, *it, nullptr)); + } + + EXPECT_FALSE(message_decoding_decode(&decodingData, 0xF0, nullptr)); + ToolboxPlaneMessages_FlightControllerSetpoint buf; + EXPECT_TRUE(message_decoding_decode(&decodingData, 0x0F, &buf)); + + EXPECT_EQ(buf.motor, 2147483647); + EXPECT_EQ(buf.pitch, 0); + EXPECT_EQ(buf.roll, 0); +} + +TEST(TEST_NAME, decode__fcs_min_pitch) { + /* + * Flight-Controller-Set-Point min power test: + * * Initialize with message_id as 0 + * * Feed 0xF0 to transition to END_FOUND, expect false + * * Feed 0x0F to transition to START_FOUND, expect false + * * Feed 0 to transition to IN_DATA, expect false + * * Feed data from fcs_min_pitch.bin, expect false for every byte + * * Feed 0xF0 to transition to IN_DATA_END_FOUND, expect false + * * Feed 0x0F to transition to START_FOUND, expect true + * * Check return message, expect: + * * motor: 0 + * * pitch: -2147483648 + * * roll: 0 + */ + message_decoding_data_t decodingData; + message_decoding_init(&decodingData, 0, ToolboxPlaneMessages_FlightControllerSetpoint_fields); + + EXPECT_FALSE(message_decoding_decode(&decodingData, 0xF0, nullptr)); + EXPECT_FALSE(message_decoding_decode(&decodingData, 0x0F, nullptr)); + EXPECT_FALSE(message_decoding_decode(&decodingData, 0, nullptr)); + + std::ifstream stream{"fcs_min_pitch.bin"}; + using c_stream_it = std::istreambuf_iterator; + + for (auto it = c_stream_it{stream}; it != c_stream_it{}; ++it) { + EXPECT_FALSE(message_decoding_decode(&decodingData, *it, nullptr)); + } + + EXPECT_FALSE(message_decoding_decode(&decodingData, 0xF0, nullptr)); + ToolboxPlaneMessages_FlightControllerSetpoint buf; + EXPECT_TRUE(message_decoding_decode(&decodingData, 0x0F, &buf)); + + EXPECT_EQ(buf.motor, 0); + EXPECT_EQ(buf.pitch, -2147483648); + EXPECT_EQ(buf.roll, 0); +} + +TEST(TEST_NAME, decode__fcs_max_pitch) { + /* + * Flight-Controller-Set-Point min power test: + * * Initialize with message_id as 0 + * * Feed 0xF0 to transition to END_FOUND, expect false + * * Feed 0x0F to transition to START_FOUND, expect false + * * Feed 0 to transition to IN_DATA, expect false + * * Feed data from fcs_max_pitch.bin, expect false for every byte + * * Feed 0xF0 to transition to IN_DATA_END_FOUND, expect false + * * Feed 0x0F to transition to START_FOUND, expect true + * * Check return message, expect: + * * motor: 0 + * * pitch: 2147483647 + * * roll: 0 + */ + message_decoding_data_t decodingData; + message_decoding_init(&decodingData, 0, ToolboxPlaneMessages_FlightControllerSetpoint_fields); + + EXPECT_FALSE(message_decoding_decode(&decodingData, 0xF0, nullptr)); + EXPECT_FALSE(message_decoding_decode(&decodingData, 0x0F, nullptr)); + EXPECT_FALSE(message_decoding_decode(&decodingData, 0, nullptr)); + + std::ifstream stream{"fcs_max_pitch.bin"}; + using c_stream_it = std::istreambuf_iterator; + + for (auto it = c_stream_it{stream}; it != c_stream_it{}; ++it) { + EXPECT_FALSE(message_decoding_decode(&decodingData, *it, nullptr)); + } + + EXPECT_FALSE(message_decoding_decode(&decodingData, 0xF0, nullptr)); + ToolboxPlaneMessages_FlightControllerSetpoint buf; + EXPECT_TRUE(message_decoding_decode(&decodingData, 0x0F, &buf)); + + EXPECT_EQ(buf.motor, 0); + EXPECT_EQ(buf.pitch, 2147483647); + EXPECT_EQ(buf.roll, 0); +} + +TEST(TEST_NAME, decode__fcs_min_roll) { + /* + * Flight-Controller-Set-Point min power test: + * * Initialize with message_id as 0 + * * Feed 0xF0 to transition to END_FOUND, expect false + * * Feed 0x0F to transition to START_FOUND, expect false + * * Feed 0 to transition to IN_DATA, expect false + * * Feed data from fcs_min_roll.bin, expect false for every byte + * * Feed 0xF0 to transition to IN_DATA_END_FOUND, expect false + * * Feed 0x0F to transition to START_FOUND, expect true + * * Check return message, expect: + * * motor: 0 + * * pitch: 0 + * * roll: -2147483648 + */ + message_decoding_data_t decodingData; + message_decoding_init(&decodingData, 0, ToolboxPlaneMessages_FlightControllerSetpoint_fields); + + EXPECT_FALSE(message_decoding_decode(&decodingData, 0xF0, nullptr)); + EXPECT_FALSE(message_decoding_decode(&decodingData, 0x0F, nullptr)); + EXPECT_FALSE(message_decoding_decode(&decodingData, 0, nullptr)); + + std::ifstream stream{"fcs_min_roll.bin"}; + using c_stream_it = std::istreambuf_iterator; + + for (auto it = c_stream_it{stream}; it != c_stream_it{}; ++it) { + EXPECT_FALSE(message_decoding_decode(&decodingData, *it, nullptr)); + } + + EXPECT_FALSE(message_decoding_decode(&decodingData, 0xF0, nullptr)); + ToolboxPlaneMessages_FlightControllerSetpoint buf; + EXPECT_TRUE(message_decoding_decode(&decodingData, 0x0F, &buf)); + + EXPECT_EQ(buf.motor, 0); + EXPECT_EQ(buf.pitch, 0); + EXPECT_EQ(buf.roll, -2147483648); +} + +TEST(TEST_NAME, decode__fcs_max_roll) { + /* + * Flight-Controller-Set-Point min power test: + * * Initialize with message_id as 0 + * * Feed 0xF0 to transition to END_FOUND, expect false + * * Feed 0x0F to transition to START_FOUND, expect false + * * Feed 0 to transition to IN_DATA, expect false + * * Feed data from fcs_max_roll.bin, expect false for every byte + * * Feed 0xF0 to transition to IN_DATA_END_FOUND, expect false + * * Feed 0x0F to transition to START_FOUND, expect true + * * Check return message, expect: + * * motor: 0 + * * pitch: 0 + * * roll: 2147483647 + */ + message_decoding_data_t decodingData; + message_decoding_init(&decodingData, 0, ToolboxPlaneMessages_FlightControllerSetpoint_fields); + + EXPECT_FALSE(message_decoding_decode(&decodingData, 0xF0, nullptr)); + EXPECT_FALSE(message_decoding_decode(&decodingData, 0x0F, nullptr)); + EXPECT_FALSE(message_decoding_decode(&decodingData, 0, nullptr)); + + std::ifstream stream{"fcs_max_roll.bin"}; + using c_stream_it = std::istreambuf_iterator; + + for (auto it = c_stream_it{stream}; it != c_stream_it{}; ++it) { + EXPECT_FALSE(message_decoding_decode(&decodingData, *it, nullptr)); + } + + EXPECT_FALSE(message_decoding_decode(&decodingData, 0xF0, nullptr)); + ToolboxPlaneMessages_FlightControllerSetpoint buf; + EXPECT_TRUE(message_decoding_decode(&decodingData, 0x0F, &buf)); + + EXPECT_EQ(buf.motor, 0); + EXPECT_EQ(buf.pitch, 0); + EXPECT_EQ(buf.roll, 2147483647); +} diff --git a/Tests/MessageEncoding.cpp b/Tests/MessageEncoding.cpp new file mode 100644 index 0000000..e28fae4 --- /dev/null +++ b/Tests/MessageEncoding.cpp @@ -0,0 +1,76 @@ +#include +#include + +extern "C" { +#include +} + +#include "FlightController.pb.h" + +TEST(TEST_NAME, encode__buffer_format) { + /* + * Encode message with all zero data and id 17, expect: + * * Returned length: 3 + * * First byte: 0x0F + * * Second byte: 17 + * * Third byte: 0xF0 + */ + std::array buf{}; + ToolboxPlaneMessages_FlightController data{}; + EXPECT_EQ(message_encode(buf.data(), buf.size(), ToolboxPlaneMessages_FlightController_fields, &data, 17), 3); + EXPECT_EQ(buf[0], 0x0F); + EXPECT_EQ(buf[1], 17); + EXPECT_EQ(buf[2], 0xF0); +} + +TEST(TEST_NAME, encode__fc_encode) { + /* + * Encode message with all zero data and id 17, expect: + * * Returned length: 3 + * * First byte: 0x0F + * * Second byte: 17 + * * Third byte: 0xF0 + */ + ToolboxPlaneMessages_FlightController data{.has_imu = true, + .imu = {.imu_ok = true, + .roll_mul_16 = 17, + .pitch_mul_16 = -18, + .yaw_mul_16 = 19, + .dRoll_mul_16 = -20, + .dPitch_mul_16 = 21, + .dYaw_mul_16 = -22, + .accX_mul_100 = 23, + .accY_mul_100 = -24, + .accZ_mul_100 = 25}, + .has_remote = true, + .remote = + { + .remote_ok = false, + .throttleMixed = -26, + .elevonLeftMixed = 27, + .elevonRightMixed = -28, + .isArmed = true, + .overrideActive = false, + }, + .motor = 29, + .servoLeft = 30, + .servoRight = 31}; + + std::ifstream stream{"fc_encode.bin"}; + using c_stream_it = std::istreambuf_iterator; + std::vector expectedData; + expectedData.emplace_back(0x0F); + expectedData.emplace_back(17); + for (auto it = c_stream_it{stream}; it != c_stream_it{}; ++it) { + expectedData.emplace_back(*it); + } + expectedData.emplace_back(0xF0); + + EXPECT_LT(expectedData.size(), ToolboxPlaneMessages_FlightController_size + 3); + std::array buf{}; + EXPECT_EQ(message_encode(buf.data(), buf.size(), ToolboxPlaneMessages_FlightController_fields, &data, 17), + expectedData.size()); + for (auto idx = 0U; idx < expectedData.size(); ++idx) { + EXPECT_EQ(buf[idx], expectedData[idx]); + } +} diff --git a/Tests/generate_tests.sh b/Tests/generate_tests.sh new file mode 100755 index 0000000..75f6952 --- /dev/null +++ b/Tests/generate_tests.sh @@ -0,0 +1,62 @@ +#!/usr/bin/env bash +echo "motor: 17, pitch: -18, roll: 19" | \ + protoc --encode=ToolboxPlaneMessages.FlightControllerSetpoint \ + --proto_path "$1" \ + Src/Messages/Definitions/FlightControllerSetpoint.proto > fcs_decode.bin + +echo "motor: -2147483648, pitch: 0, roll: 0" | \ + protoc --encode=ToolboxPlaneMessages.FlightControllerSetpoint \ + --proto_path "$1" \ + Src/Messages/Definitions/FlightControllerSetpoint.proto > fcs_min_power.bin + +echo "motor: 2147483647, pitch: 0, roll: 0" | \ + protoc --encode=ToolboxPlaneMessages.FlightControllerSetpoint \ + --proto_path "$1" \ + Src/Messages/Definitions/FlightControllerSetpoint.proto > fcs_max_power.bin + +echo "motor: 0, pitch: -2147483648, roll: 0" | \ + protoc --encode=ToolboxPlaneMessages.FlightControllerSetpoint \ + --proto_path "$1" \ + Src/Messages/Definitions/FlightControllerSetpoint.proto > fcs_min_pitch.bin + +echo "motor: 0, pitch: 2147483647, roll: 0" | \ + protoc --encode=ToolboxPlaneMessages.FlightControllerSetpoint \ + --proto_path "$1" \ + Src/Messages/Definitions/FlightControllerSetpoint.proto > fcs_max_pitch.bin + +echo "motor: 0, pitch: 0, roll: -2147483648" | \ + protoc --encode=ToolboxPlaneMessages.FlightControllerSetpoint \ + --proto_path "$1" \ + Src/Messages/Definitions/FlightControllerSetpoint.proto > fcs_min_roll.bin + +echo "motor: 0, pitch: 0, roll: 2147483647" | \ + protoc --encode=ToolboxPlaneMessages.FlightControllerSetpoint \ + --proto_path "$1" \ + Src/Messages/Definitions/FlightControllerSetpoint.proto > fcs_max_roll.bin + +echo "imu: { + imu_ok: true, + roll_mul_16: 17, + pitch_mul_16: -18 + yaw_mul_16: 19, + dRoll_mul_16: -20, + dPitch_mul_16: 21, + dYaw_mul_16: -22 + accX_mul_100: 23, + accY_mul_100: -24, + accZ_mul_100: 25 + }, + remote: { + remote_ok: false; + throttleMixed: -26; + elevonLeftMixed: 27; + elevonRightMixed: -28; + isArmed: true; + overrideActive: false; + }, + motor: 29, + servoLeft: 30, + servoRight: 31" | \ + protoc --encode=ToolboxPlaneMessages.FlightController \ + --proto_path "$1" \ + Src/Messages/Definitions/FlightController.proto > fc_encode.bin