diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt index 8726a49..b9f1d7f 100644 --- a/main/CMakeLists.txt +++ b/main/CMakeLists.txt @@ -1,4 +1,4 @@ -idf_component_register(SRCS "main.c" "uart_handler.c" "communication_handler.c" "uart_prot.c" "client_handler.c" "message_parser.c" "message_builder.c" "message_handler.c" +idf_component_register(SRCS "main.c" "uart_handler.c" "communication_handler.c" "client_handler.c" "message_parser.c" "message_builder.c" "message_handler.c" INCLUDE_DIRS ".") # Get the short Git commit hash of the current HEAD. diff --git a/main/message_handler.c b/main/message_handler.c new file mode 100644 index 0000000..d4c5555 --- /dev/null +++ b/main/message_handler.c @@ -0,0 +1,62 @@ +#include "message_handler.h" +#include "esp_log.h" +#include "freertos/idf_additions.h" +#include "uart_handler.h" + +static struct MessageBroker mr; +static char *TAG = "ALOX - Message Handler"; + +void InitMessageBroker() { + mr.num_direct_callbacks = 0; + mr.num_task_callbacks = 0; + return; +} + +void RegisterCallback(uint8_t msgid, RegisterFunctionCallback callback) { + mr.FunctionList[mr.num_direct_callbacks].MSGID = msgid; + mr.FunctionList[mr.num_direct_callbacks].callback = callback; + mr.num_direct_callbacks++; + return; +} + +void RegisterTask(uint8_t msgid, RegisterTaskCallback callback) { + mr.TaskList[mr.num_task_callbacks].MSGID = msgid; + mr.TaskList[mr.num_task_callbacks].task = callback; + mr.num_task_callbacks++; + return; +} + +void MessageBrokerTask(void *param) { + ParsedMessage_t received_msg; + QueueHandle_t msg_queue = *(QueueHandle_t *)param; + + if (msg_queue == NULL) { + ESP_LOGE(TAG, "Message queue not initialized. Terminating task."); + vTaskDelete(NULL); + } + + ESP_LOGI(TAG, "Message broker task started."); + + while (1) { + if (xQueueReceive(msg_queue, &received_msg, portMAX_DELAY)) { + ESP_LOGI(TAG, "Received message from queue: MSGID=0x%02X, Length=%u", + received_msg.msgid, received_msg.payload_len); + + for (int i = 0; i < mr.num_direct_callbacks; i++) { + if (mr.FunctionList[i].MSGID == received_msg.msgid) { + mr.FunctionList[i].callback(received_msg.msgid, received_msg.data, + received_msg.payload_len); + } + } + for (int i = 0; i < mr.num_direct_callbacks; i++) { + if (mr.FunctionList[i].MSGID == received_msg.msgid) { + // TODO: Not yet implemented + // Only send data to task, task should be created beforhead and wait + // for new data in the queue. + } + } + } + } +} + +void SendMessage(const uint8_t *buffer, size_t length); diff --git a/main/message_handler.h b/main/message_handler.h new file mode 100644 index 0000000..b101cd9 --- /dev/null +++ b/main/message_handler.h @@ -0,0 +1,38 @@ +#ifndef _MESSAGE_HANDLER_HEADER +#define _MESSAGE_HANDLER_HEADER + +#include "freertos/idf_additions.h" +#include +#include + +typedef void (*RegisterFunctionCallback)(uint8_t msgid, const uint8_t *payload, + size_t payload_len); +typedef void (*RegisterTaskCallback)(uint8_t msgid, const uint8_t *payload, + size_t payload_len); + +struct RegisterdFunction { + uint8_t MSGID; + RegisterFunctionCallback callback; +}; + +struct RegisterdTask { + uint8_t MSGID; + RegisterTaskCallback task; +}; + +struct MessageBroker { + struct RegisterdFunction FunctionList[64]; + uint8_t num_direct_callbacks; + struct RegisterdTask TaskList[64]; + uint8_t num_task_callbacks; +}; + +typedef void (*SendMessageHookCallback)(const uint8_t *buffer, size_t length); + +void InitMessageBroker(); +void RegisterCallback(uint8_t msgid, RegisterFunctionCallback callback); +void RegisterTask(uint8_t msgid, RegisterTaskCallback callback); +void SendMessage(const uint8_t *buffer, size_t length); +void MessageBrokerTask(void *param); + +#endif diff --git a/main/message_parser.c b/main/message_parser.c new file mode 100644 index 0000000..921d74d --- /dev/null +++ b/main/message_parser.c @@ -0,0 +1,112 @@ +#include "message_parser.h" +#include +#include + +MessageReceivedCallback on_message_received = NULL; +MessageFailCallback on_message_fail = NULL; + +struct MessageReceive InitMessageReceive() { + struct MessageReceive mr = { + .state = WaitingForStartByte, // Startzustand des Parsers + .error = NoError, // Kein Fehler zu Beginn + .messageid = 0, // MSGID auf Standardwert setzen + // .message Array muss nicht explizit initialisiert werden, da es bei + // jedem Start geleert wird + .index = 0, // Index für das Nachrichten-Array initialisieren + .checksum = 0 // Checksumme initialisieren + }; + return mr; +} + +// Registrierungsfunktionen für die Callbacks +void register_message_callback(MessageReceivedCallback callback) { + on_message_received = callback; +} + +void register_message_fail_callback(MessageFailCallback callback) { + on_message_fail = callback; +} + +void parse_byte(struct MessageReceive *mr, uint8_t pbyte) { + switch (mr->state) { + case WaitingForStartByte: + if (pbyte == StartByte) { + mr->index = 0; + mr->checksum = 0; + mr->state = GetMessageType; + } + break; + case EscapedMessageType: + mr->messageid = pbyte; + mr->checksum ^= pbyte; + mr->state = InPayload; + break; + case GetMessageType: + if (pbyte == EscapeByte) { + mr->state = EscapedMessageType; + return; + } + if (pbyte == StartByte || pbyte == EndByte) { + mr->state = WaitingForStartByte; + mr->error = UnexpectedCommandByte; + if (on_message_received) { + on_message_fail(mr->messageid, mr->message, mr->index, mr->error); + } + return; + } + mr->messageid = pbyte; + mr->checksum ^= pbyte; + mr->state = InPayload; + break; + case EscapePayloadByte: + mr->message[mr->index++] = pbyte; + mr->checksum ^= pbyte; + mr->state = InPayload; + break; + case InPayload: + if (pbyte == EscapeByte) { + mr->state = EscapePayloadByte; + return; + } + if (pbyte == StartByte) { + mr->state = WaitingForStartByte; + mr->error = UnexpectedCommandByte; + if (on_message_received) { + on_message_fail(mr->messageid, mr->message, mr->index, mr->error); + } + return; + } + if (pbyte == EndByte) { + if (mr->checksum != 0x00) { + // Checksum failure + // The Checksum gets treated like a normal byte until the end byte + // accours. Therefore the last byte xor'ed to the checksum ist the + // checksum so the checksum must be Zero. + mr->state = WaitingForStartByte; + mr->error = WrongCheckSum; + if (on_message_received) { + on_message_fail(mr->messageid, mr->message, mr->index, mr->error); + } + return; + } + if (on_message_received) { + on_message_received(mr->messageid, mr->message, + mr->index - 1); // remove checksum byte by just + // setting the length of the message + } + mr->state = WaitingForStartByte; + } + if (mr->index < MAX_TOTAL_CONTENT_LENGTH) { + mr->message[mr->index++] = pbyte; + mr->checksum ^= pbyte; + } else { + mr->state = WaitingForStartByte; + mr->error = MessageToLong; + if (on_message_received) { + on_message_fail(mr->messageid, mr->message, mr->index, mr->error); + } + return; + } + break; + } +} diff --git a/main/message_parser.h b/main/message_parser.h new file mode 100644 index 0000000..8b1c0cf --- /dev/null +++ b/main/message_parser.h @@ -0,0 +1,52 @@ +#ifndef _MESSAGE_PARSER_HEADER +#define _MESSAGE_PARSER_HEADER + +#include +#include + +#define MAX_MESSAGE_PAYLOAD_LENGTH 128 +#define MAX_TOTAL_CONTENT_LENGTH (MAX_MESSAGE_PAYLOAD_LENGTH + 1) + +enum ParserState { + WaitingForStartByte, + GetMessageType, + EscapedMessageType, + EscapePayloadByte, + InPayload, +}; + +enum ParserError { + NoError, + WrongCheckSum, + MessageToLong, + UnexpectedCommandByte, +}; + +typedef enum { + StartByte = 0xAA, + EscapeByte = 0xBB, + EndByte = 0xCC, +} MessageBytes; + +struct MessageReceive { + enum ParserState state; + enum ParserError error; + uint8_t messageid; + uint8_t message[MAX_MESSAGE_PAYLOAD_LENGTH]; + uint8_t index; + uint8_t checksum; +}; + +typedef void (*MessageReceivedCallback)(uint8_t msgid, const uint8_t *payload, + size_t payload_len); +typedef void (*MessageFailCallback)(uint8_t msgid, const uint8_t *payload, + size_t payload_len, enum ParserError error); + +struct MessageReceive InitMessageReceive(); + +void register_message_callback(MessageReceivedCallback callback); +void register_message_fail_callback(MessageFailCallback callback); + +void parse_byte(struct MessageReceive *mr, uint8_t pbyte); + +#endif diff --git a/main/uart_handler.c b/main/uart_handler.c index 97223bb..92a1600 100644 --- a/main/uart_handler.c +++ b/main/uart_handler.c @@ -1,19 +1,23 @@ #include "driver/gpio.h" #include "driver/uart.h" #include "esp_log.h" +#include "esp_log_buffer.h" #include "freertos/idf_additions.h" #include "hal/uart_types.h" +#include "message_handler.h" +#include "message_parser.h" #include "nvs_flash.h" #include "portmacro.h" #include #include +#include "message_parser.h" #include "uart_handler.h" -#include "uart_prot.h" static const char *TAG = "ALOX - UART"; +static QueueHandle_t parsed_message_queue; -void init_uart() { +void init_uart(QueueHandle_t msg_queue_handle) { uart_config_t uart_config = {.baud_rate = 115200, .data_bits = UART_DATA_8_BITS, .parity = UART_PARITY_DISABLE, @@ -25,12 +29,16 @@ void init_uart() { uart_set_pin(MASTER_UART, TXD_PIN, RXD_PIN, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE); - message_handler_init(); + parsed_message_queue = msg_queue_handle; + register_message_callback(HandleMessageReceivedCallback); + register_message_fail_callback(HandleMessageFailCallback); + xTaskCreate(uart_read_task, "Read Uart", 4096, NULL, 1, NULL); } void uart_read_task(void *param) { - QueueHandle_t inputQueue = message_handler_get_input_queue(); + // Send all Input from Uart to the Message Handler for Parsing + struct MessageReceive mr = InitMessageReceive(); uint8_t *data = (uint8_t *)malloc(BUF_SIZE); int len = 0; while (1) { @@ -39,32 +47,44 @@ void uart_read_task(void *param) { uart_read_bytes(MASTER_UART, data, BUF_SIZE, (20 / portTICK_PERIOD_MS)); if (len > 0) { for (int i = 0; i < len; ++i) { - BaseType_t res = xQueueSend(inputQueue, &data[i], 0); - if (res == errQUEUE_FULL) { - ESP_LOGW(TAG, "inputQueue full"); - } + parse_byte(&mr, data[i]); } } } } -void uart_status_task(void *param) { - while (1) { - uart_write_bytes(MASTER_UART, "c1,status,0\n\r", sizeof("c1,status,0\n\r")); - vTaskDelay(1000 / portTICK_PERIOD_MS); +// TODO: Remove this? or handle message sending in any other way reduce +// abstraction hell +void send_message_hook(const uint8_t *buffer, size_t length) { + uart_write_bytes(MASTER_UART, buffer, length); +} + +void HandleMessageReceivedCallback(uint8_t msgid, const uint8_t *payload, + size_t payload_len) { + ESP_LOGI(TAG, "GOT UART MESSAGE MSGID: %02X, Len: %u bytes \nMSG: ", msgid, + payload_len, payload); + ESP_LOG_BUFFER_HEX(TAG, payload, payload_len); + + ParsedMessage_t msg_to_send; + msg_to_send.msgid = msgid; + msg_to_send.payload_len = payload_len; + memcpy(msg_to_send.data, payload, payload_len); + + if (xQueueSend(parsed_message_queue, &msg_to_send, portMAX_DELAY) != pdPASS) { + // Fehlerbehandlung: Queue voll oder Senden fehlgeschlagen + ESP_LOGE(TAG, "Failed to send parsed message to queue."); } + + return; } -void send_client_info(int clientid, bool isAvailable, TickType_t lastPing) { - char buf[128]; - snprintf(buf, sizeof(buf), "c%d,status,2,%d,%u\r\n", clientid, - isAvailable ? 1 : 0, (unsigned int)lastPing); - uart_write_bytes(MASTER_UART, buf, strlen(buf)); -} +void HandleMessageFailCallback(uint8_t msgid, const uint8_t *payload, + size_t payload_len, enum ParserError error) { + ESP_LOGE( + TAG, + "UART MESSAGE Parsing Failed MSGID: %02X, Len: %u, ERROR: %X, \nMSG: ", + msgid, payload_len, error); + ESP_LOG_BUFFER_HEX(TAG, payload, payload_len); -void esp_send_message_hook(ESPTOPCBaseMessage *msg) { - // serialize + send via UART - uint8_t buffer[128]; - uart_write_bytes(UART_NUM_1, (const char *)buffer, - sizeof(ESPTOPCBaseMessage)); + return; } diff --git a/main/uart_handler.h b/main/uart_handler.h index 8e01578..d4c4af8 100644 --- a/main/uart_handler.h +++ b/main/uart_handler.h @@ -1,16 +1,30 @@ #ifndef UART_HANDLER_H #define UART_HANDLER_H +#include "freertos/idf_additions.h" +#include "message_parser.h" +#include +#include + #define MASTER_UART UART_NUM_1 #define TXD_PIN (GPIO_NUM_1) #define RXD_PIN (GPIO_NUM_2) -#define BUF_SIZE (1024) +#define BUF_SIZE (256) -void init_uart(); +typedef struct { + uint8_t msgid; + size_t payload_len; + uint8_t data[MAX_MESSAGE_PAYLOAD_LENGTH]; +} ParsedMessage_t; + +void init_uart(QueueHandle_t msg_queue_handle); void uart_read_task(void *param); -void uart_status_task(void *param); +void uart_send_task(void *param); -void send_client_info(int clientid, bool isAvailable, TickType_t lastPing); +void HandleMessageReceivedCallback(uint8_t msgid, const uint8_t *payload, + size_t payload_len); +void HandleMessageFailCallback(uint8_t msgid, const uint8_t *payload, + size_t payload_len, enum ParserError error); #endif diff --git a/main/uart_prot.c b/main/uart_prot.c deleted file mode 100644 index 65ef861..0000000 --- a/main/uart_prot.c +++ /dev/null @@ -1,97 +0,0 @@ -#include "uart_prot.h" -#include - -#define MSG_QUEUE_LEN 64 -static QueueHandle_t input_queue; -static QueueHandle_t output_queue; - -QueueHandle_t message_handler_get_input_queue(void) { - return input_queue; -} - -QueueHandle_t message_handler_get_output_queue(void) { - return output_queue; -} - -void message_handler_init(void) { - input_queue = xQueueCreate(MSG_QUEUE_LEN, sizeof(uint8_t)); - output_queue = xQueueCreate(MSG_QUEUE_LEN, sizeof(uint8_t)); -} - -void message_handler_task(void *param) { - uint8_t byte; - while (1) { - if (xQueueReceive(input_queue, &byte, portMAX_DELAY)) { - // handle byte, check message recieve with start and stop byte length and crc - } - } -} - -// Message Dispatcher -void dispatch_message(uint8_t msg_id, void *payload) { - switch (msg_id) { - case RequestPing: - if (on_request_ping) - on_request_ping((RequestPingPayload *)payload); - break; - case RequestStatus: - if (on_request_status) - on_request_status((RequestStatusPayload *)payload); - break; - case PrepareFirmwareUpdate: - if (on_prepare_firmware_update) - on_prepare_firmware_update((PrepareFirmwareUpdatePayload *)payload); - break; - case FirmwareUpdateLine: - if (on_firmware_update_line) - on_firmware_update_line((FirmwareUpdateLinePayload *)payload); - break; - default: - // Unknown message - break; - } -} - -// Generic Send Function -void send_message(ESP_TO_PC_MESSAGE_IDS msgid, PayloadUnion *payload) { - ESPTOPCBaseMessage mes; - mes.Version = 1; - mes.MessageID = msgid; - mes.Payload = *payload; - - esp_send_message_hook(&mes); -} - -// Sepzific Send Functions -void send_clients(uint8_t clientCount, uint32_t clientAvaiableBitMask) { - ClientsPayload payload; - - // Payload-Daten zuweisen - payload.clientCount = clientCount; - payload.clientAvaiableBitMask = clientAvaiableBitMask; - - // Nachricht senden - send_message(Clients, (PayloadUnion *)&payload); -} - -void send_status(uint8_t clientId, uint8_t *mac) { - StatusPayload payload; - - // Payload-Daten zuweisen - payload.clientId = clientId; - memcpy(payload.mac, mac, 6); - - // Nachricht senden - send_message(Status, (PayloadUnion *)&payload); -} - -void send_pong(uint8_t clientId, uint32_t ping) { - PongPayload payload; - - // Payload-Daten zuweisen - payload.clientId = clientId; - payload.ping = ping; - - // Nachricht senden - send_message(Pong, (PayloadUnion *)&payload); -} diff --git a/main/uart_prot.h b/main/uart_prot.h deleted file mode 100644 index 9714830..0000000 --- a/main/uart_prot.h +++ /dev/null @@ -1,100 +0,0 @@ -#ifndef _PROTO_HEADER -#define _PROTO_HEADER - -#include "freertos/idf_additions.h" -#include - -void message_handler_init(void); -QueueHandle_t message_handler_get_input_queue(void); -QueueHandle_t message_handler_get_output_queue(void); - -// MessageIDs -typedef enum { - RequestPing = 0xE1, - RequestStatus = 0xE2, - PrepareFirmwareUpdate = 0xF1, - FirmwareUpdateLine = 0xF2, -} PC_TO_ESP_MESSAGE_IDS; - -typedef enum { - Clients = 0xE1, - Status = 0xE2, - Pong = 0xD1, -} ESP_TO_PC_MESSAGE_IDS; - -// Payloads for single Messages -typedef struct { - uint8_t clientId; -} RequestPingPayload; - -typedef struct { - uint8_t clientId; -} RequestStatusPayload; - -typedef struct { - // empty payload -} PrepareFirmwareUpdatePayload; - -typedef struct { - uint8_t data[240]; -} FirmwareUpdateLinePayload; - -typedef struct { - uint8_t clientCount; - uint32_t clientAvaiableBitMask; -} ClientsPayload; - -typedef struct { - uint8_t clientId; - uint8_t mac[6]; -} StatusPayload; - -typedef struct { - uint8_t clientId; - uint32_t ping; -} PongPayload; - -// Union for all the Payloads -typedef union { - RequestPingPayload request_ping; - RequestStatusPayload request_status; - PrepareFirmwareUpdatePayload prepare_firmware_update; - FirmwareUpdateLinePayload firmware_update_line; - ClientsPayload clients; - StatusPayload status; - PongPayload pong; -} PayloadUnion; - -// Base Message that can hold all Payloads -typedef struct { - uint8_t Version; - PC_TO_ESP_MESSAGE_IDS MessageID; - uint8_t Length; - PayloadUnion Payload; -} PCTOESPBaseMessage; - -typedef struct { - uint8_t Version; - ESP_TO_PC_MESSAGE_IDS MessageID; - uint8_t Length; - PayloadUnion Payload; -} ESPTOPCBaseMessage; - -// deklarierte Hook-Signatur -void esp_send_message_hook(ESPTOPCBaseMessage *msg); - -// Generic Send Function Prototype -void send_message(ESP_TO_PC_MESSAGE_IDS msgid, PayloadUnion *payload); - -// Spezific Send Functions Prototype -void send_clients(uint8_t clientCount, uint32_t clientAvaiableBitMask); -void send_status(uint8_t clientId, uint8_t *mac); -void send_pong(uint8_t clientId, uint32_t ping); - -// Prototypes for Message Recieve Handler to be set in user code -void (*on_request_ping)(RequestPingPayload *); -void (*on_request_status)(RequestStatusPayload *); -void (*on_prepare_firmware_update)(PrepareFirmwareUpdatePayload *); -void (*on_firmware_update_line)(FirmwareUpdateLinePayload *); - -#endif diff --git a/tests/test_parser.c b/tests/test_parser.c index bf6056b..e864238 100644 --- a/tests/test_parser.c +++ b/tests/test_parser.c @@ -1,85 +1,452 @@ #include "Unity/src/unity.h" -#include "message_parser.h" +#include "message_parser.h" // Stellt sicher, dass deine Header-Datei message_parser.h korrekt ist #include +#include // Für memcpy + +// Globale Variablen für Callback-Überprüfung +static uint8_t received_msgid = 0xFF; +static uint8_t + received_payload[MAX_TOTAL_CONTENT_LENGTH]; // Muss groß genug sein +static size_t received_payload_len = 0; +static enum ParserError received_error = NoError; +static bool message_received_flag = false; +static bool message_fail_flag = false; + +// Mock-Implementierungen für die Callbacks +void mock_on_message_received(uint8_t msgid, const uint8_t *payload, + size_t payload_len) { + received_msgid = msgid; + received_payload_len = payload_len; + // Sicherstellen, dass der Puffer nicht überläuft + memcpy(received_payload, payload, + (payload_len < MAX_TOTAL_CONTENT_LENGTH) ? payload_len + : MAX_TOTAL_CONTENT_LENGTH); + message_received_flag = true; +} + +void mock_on_message_fail(uint8_t msgid, const uint8_t *payload, + size_t payload_len, enum ParserError error) { + received_msgid = msgid; // Auch bei Fehlern kann die ID relevant sein + received_payload_len = payload_len; + // Auch hier, um sicherzustellen, dass wir den Zustand des Puffers beim Fehler + // sehen können + memcpy(received_payload, payload, + (payload_len < MAX_TOTAL_CONTENT_LENGTH) ? payload_len + : MAX_TOTAL_CONTENT_LENGTH); + received_error = error; + message_fail_flag = true; +} + +// --- UNITY SETUP/TEARDOWN --- +void setUp(void) { + // Reset der globalen Variablen vor jedem Test + received_msgid = 0xFF; + received_payload_len = 0; + received_error = NoError; + message_received_flag = false; + message_fail_flag = false; + memset(received_payload, 0, MAX_TOTAL_CONTENT_LENGTH); + + // Registrierung der Mock-Callbacks (muss vor den Tests erfolgen) + register_message_callback(mock_on_message_received); + register_message_fail_callback(mock_on_message_fail); +} -void setUp(void) {} // optional void tearDown(void) {} // optional -// Gültige Nachricht mit Payload -void test_valid_message_parses_correctly(void) { - struct MessageRecieve mr = { - .state = WaitingForStartByte, - .messageid = 0, - .index = 0, - .checksum = 0, +// --- Hilfsfunktion zur Checksummenberechnung (für Tests) --- +// Berechnet die Checksumme für MSGID + Payload + Checksummen-Byte +// Ergibt 0x00, wenn die gesamte Kette XORiert wird +uint8_t calculate_test_checksum_final(uint8_t msgid, const uint8_t *payload, + size_t payload_len, + uint8_t actual_checksum_byte) { + uint8_t cs = msgid; + for (size_t i = 0; i < payload_len; ++i) { + cs ^= payload[i]; + } + cs ^= + actual_checksum_byte; // Das gesendete Checksummen-Byte wird auch XORiert + return cs; +} +// Hilfsfunktion zur Berechnung des *zu sendenden* Checksummen-Bytes +// Dies ist der Wert, der im Frame an der Checksummen-Position steht, +// sodass die finale XOR-Summe der Nutzdaten (MSGID + Payload + dieses Byte) +// 0x00 ergibt. +uint8_t calculate_payload_checksum_byte(uint8_t msgid, const uint8_t *payload, + size_t payload_len) { + uint8_t cs = msgid; + for (size_t i = 0; i < payload_len; ++i) { + cs ^= payload[i]; + } + return cs; // Dies ist der Wert, der gesendet werden muss, damit die finale + // XOR-Summe 0x00 wird +} + +// Test 1: Gültige Nachricht mit Payload +void test_1_valid_message_parses_correctly(void) { + struct MessageReceive mr = InitMessageReceive(); + + uint8_t msgid = 0x01; + uint8_t payload[] = {0x01, 0x02, 0x03}; + size_t payload_len = sizeof(payload); + + // Berechne das Checksummen-Byte, das gesendet werden muss + uint8_t checksum_byte_to_send = + calculate_payload_checksum_byte(msgid, payload, payload_len); + + uint8_t full_message[] = { + StartByte, // 0xAA + msgid, // 0x01 + 0x01, + 0x02, + 0x03, // Payload + checksum_byte_to_send, // Das Checksummen-Byte + EndByte // 0xCC }; - uint8_t msg[] = {0xAA, 0x01, 0x03, 0x01, 0x02, 0x03, 0x00}; - msg[6] = msg[1] ^ msg[2] ^ msg[3] ^ msg[4] ^ msg[5]; // korrekte Checksumme + for (uint8_t i = 0; i < sizeof(full_message); ++i) { + parse_byte(&mr, full_message[i]); + } - for (uint8_t i = 0; i < sizeof(msg); ++i) - parse_byte(&mr, msg[i]); - - TEST_ASSERT_EQUAL_UINT8(1, mr.messageid); - TEST_ASSERT_EQUAL_UINT8(3, mr.length); - uint8_t expected[] = {1, 2, 3}; - TEST_ASSERT_EQUAL_UINT8_ARRAY(expected, mr.message, 3); + TEST_ASSERT_TRUE(message_received_flag); + TEST_ASSERT_FALSE(message_fail_flag); + TEST_ASSERT_EQUAL_UINT8(msgid, received_msgid); + TEST_ASSERT_EQUAL_UINT8(payload_len, received_payload_len); // Payload-Länge + TEST_ASSERT_EQUAL_UINT8_ARRAY(payload, received_payload, payload_len); + TEST_ASSERT_EQUAL_UINT8(WaitingForStartByte, mr.state); + TEST_ASSERT_EQUAL_UINT8(NoError, mr.error); } -// Ungültige Checksumme – Zustand wird zurückgesetzt -void test_invalid_checksum_resets_state(void) { - struct MessageRecieve mr = { - .state = WaitingForStartByte, - .messageid = 0, - .index = 0, - .checksum = 0, +// Test 2: Ungültige Checksumme – Fehler gemeldet und Zustand zurückgesetzt +void test_2_invalid_checksum_resets_state(void) { + struct MessageReceive mr = InitMessageReceive(); + + uint8_t msgid = 0x02; + uint8_t payload[] = {0x10, 0x20}; + size_t payload_len = sizeof(payload); + uint8_t wrong_checksum_byte = 0x01; // Absichtlich falsche Checksumme + + uint8_t full_message[] = {StartByte, + msgid, + 0x10, + 0x20, + wrong_checksum_byte, // Falsches Checksummen-Byte + EndByte}; + + for (uint8_t i = 0; i < sizeof(full_message); ++i) { + parse_byte(&mr, full_message[i]); + } + + TEST_ASSERT_FALSE(message_received_flag); + TEST_ASSERT_TRUE(message_fail_flag); + TEST_ASSERT_EQUAL_UINT8(msgid, received_msgid); + // received_payload_len sollte die Länge der Daten sein, die bis zum Fehler + // empfangen wurden, d.h., Payload-Länge + das falsche Checksummen-Byte. + TEST_ASSERT_EQUAL_UINT8(payload_len + 1, received_payload_len); + TEST_ASSERT_EQUAL_UINT8(WrongCheckSum, received_error); + TEST_ASSERT_EQUAL_UINT8(WaitingForStartByte, mr.state); + TEST_ASSERT_EQUAL_UINT8(WrongCheckSum, mr.error); +} + +// Test 3: Gültige Nachricht ohne Payload (Länge 0) +void test_3_zero_length_message(void) { + struct MessageReceive mr = InitMessageReceive(); + + uint8_t msgid = 0x03; + uint8_t payload[] = {}; // Leerer Payload + size_t payload_len = sizeof(payload); + + uint8_t checksum_byte_to_send = + calculate_payload_checksum_byte(msgid, payload, payload_len); + + uint8_t full_message[] = { + StartByte, msgid, + checksum_byte_to_send, // Checksummen-Byte (hier gleich MSGID, da Payload + // leer) + EndByte}; + + for (uint8_t i = 0; i < sizeof(full_message); ++i) { + parse_byte(&mr, full_message[i]); + } + + TEST_ASSERT_TRUE(message_received_flag); + TEST_ASSERT_FALSE(message_fail_flag); + TEST_ASSERT_EQUAL_UINT8(msgid, received_msgid); + TEST_ASSERT_EQUAL_UINT8(0, received_payload_len); // Payload-Länge ist 0 + TEST_ASSERT_EQUAL_UINT8(WaitingForStartByte, mr.state); + TEST_ASSERT_EQUAL_UINT8(NoError, mr.error); +} + +// Test 4: Escapete MSGID (0xAA im MSGID-Feld) +void test_4_escaped_message_id(void) { + struct MessageReceive mr = InitMessageReceive(); + + uint8_t msgid = 0xAA; // MSGID ist ein Steuerzeichen, muss escapet werden + uint8_t payload[] = {0x42}; + size_t payload_len = sizeof(payload); + + uint8_t checksum_byte_to_send = + calculate_payload_checksum_byte(msgid, payload, payload_len); + + uint8_t full_message[] = {StartByte, + EscapeByte, + msgid, // Escapete MSGID (0xBB 0xAA) + 0x42, // Payload + checksum_byte_to_send, + EndByte}; + + for (uint8_t i = 0; i < sizeof(full_message); ++i) { + parse_byte(&mr, full_message[i]); + } + + TEST_ASSERT_TRUE(message_received_flag); + TEST_ASSERT_FALSE(message_fail_flag); + TEST_ASSERT_EQUAL_UINT8(msgid, received_msgid); + TEST_ASSERT_EQUAL_UINT8(payload_len, received_payload_len); + TEST_ASSERT_EQUAL_UINT8_ARRAY(payload, received_payload, payload_len); + TEST_ASSERT_EQUAL_UINT8(WaitingForStartByte, mr.state); + TEST_ASSERT_EQUAL_UINT8(NoError, mr.error); +} + +// Test 5: Escapetes Payload-Byte (z.B. ein StartByte im Payload) +void test_5_escaped_payload_byte(void) { + struct MessageReceive mr = InitMessageReceive(); + + uint8_t msgid = 0x05; + uint8_t payload[] = { + 0x11, StartByte}; // StartByte (0xAA) im Payload, muss escapet werden + size_t payload_len = sizeof(payload); + + uint8_t checksum_byte_to_send = + calculate_payload_checksum_byte(msgid, payload, payload_len); + + uint8_t full_message[] = { + StartByte, + msgid, + 0x11, + EscapeByte, + StartByte, // Escaptes StartByte (0xBB 0xAA) im Payload + checksum_byte_to_send, + EndByte}; + + for (uint8_t i = 0; i < sizeof(full_message); ++i) { + parse_byte(&mr, full_message[i]); + } + + TEST_ASSERT_TRUE(message_received_flag); + TEST_ASSERT_FALSE(message_fail_flag); + TEST_ASSERT_EQUAL_UINT8(msgid, received_msgid); + TEST_ASSERT_EQUAL_UINT8(payload_len, received_payload_len); + TEST_ASSERT_EQUAL_UINT8_ARRAY(payload, received_payload, payload_len); + TEST_ASSERT_EQUAL_UINT8(WaitingForStartByte, mr.state); + TEST_ASSERT_EQUAL_UINT8(NoError, mr.error); +} + +// Test 6: Escapetes Checksummen-Byte (z.B. ein EndByte als Checksumme) +void test_6_escaped_checksum_byte(void) { + struct MessageReceive mr = InitMessageReceive(); + + uint8_t msgid = 0x01; + uint8_t payload[] = { + 0xCD}; // payload[0] ^ msgid = 0xCD ^ 0x01 = 0xCC (EndByte) + size_t payload_len = sizeof(payload); + + uint8_t checksum_byte_to_send = calculate_payload_checksum_byte( + msgid, payload, payload_len); // Dies ist 0xCC + + uint8_t full_message[] = { + StartByte, + msgid, + payload[0], // Payload + EscapeByte, + checksum_byte_to_send, // Escaptes 0xCC als Checksummen-Byte (0xBB 0xCC) + EndByte}; + + for (uint8_t i = 0; i < sizeof(full_message); ++i) { + parse_byte(&mr, full_message[i]); + } + + TEST_ASSERT_TRUE(message_received_flag); + TEST_ASSERT_FALSE(message_fail_flag); + TEST_ASSERT_EQUAL_UINT8(msgid, received_msgid); + TEST_ASSERT_EQUAL_UINT8(payload_len, received_payload_len); + TEST_ASSERT_EQUAL_UINT8_ARRAY(payload, received_payload, payload_len); + TEST_ASSERT_EQUAL_UINT8(WaitingForStartByte, mr.state); + TEST_ASSERT_EQUAL_UINT8(NoError, mr.error); +} + +// Test 7: Nachricht zu lang (Pufferüberlauf) +void test_7_message_too_long(void) { + struct MessageReceive mr = + InitMessageReceive(); // mr.max_total_content_length wird auf + // MAX_TOTAL_CONTENT_LENGTH gesetzt + + uint8_t msgid = 0x07; + // Dieser Payload ist absichtlich 1 Byte zu lang für + // MAX_MESSAGE_PAYLOAD_LENGTH. D.h., der gesamte Inhalt für mr.message[] + // (Payload + Checksumme) ist MAX_TOTAL_CONTENT_LENGTH + 1 Bytes lang. Dadurch + // wird der Puffer definitiv überlaufen. + uint8_t oversized_data_for_buffer[MAX_TOTAL_CONTENT_LENGTH + + 1]; // Ein Byte zu viel für mr.message[] + for (size_t i = 0; i < sizeof(oversized_data_for_buffer); ++i) { + oversized_data_for_buffer[i] = (uint8_t)(i + 1); // Beliebige Daten + } + + // Wir brauchen eine korrekte Checksumme, auch wenn die Nachricht zu lang ist, + // da der Sender diese theoretisch senden würde. + // Die Berechnung erfolgt über die tatsächlich gesendeten Payload-Daten (die + // zu lang sind). + uint8_t checksum_byte_for_oversized_msg = calculate_payload_checksum_byte( + msgid, oversized_data_for_buffer, MAX_MESSAGE_PAYLOAD_LENGTH + 1); + + // Simuliere den Versand der Nachricht Byte für Byte + parse_byte(&mr, StartByte); // mr.state = GetMessageType + parse_byte(&mr, msgid); // mr.state = InPayload, mr.messageid = 0x07 + + // Sende MAX_TOTAL_CONTENT_LENGTH Bytes, die den Puffer `mr.message` + // vollständig füllen. Index läuft von 0 bis MAX_TOTAL_CONTENT_LENGTH-1. + for (size_t i = 0; i < MAX_TOTAL_CONTENT_LENGTH; ++i) { + // Hier schicken wir die ersten MAX_TOTAL_CONTENT_LENGTH Bytes des + // übergroßen Payloads (inklusive dem eigentlichen Checksummen-Byte an + // Position MAX_MESSAGE_PAYLOAD_LENGTH). Das wird den Puffer anfüllen, aber + // noch keinen Overflow melden. + parse_byte(&mr, oversized_data_for_buffer[i]); + } + + // An diesem Punkt sollte mr.index = MAX_TOTAL_CONTENT_LENGTH sein. + // Der Puffer `mr.message` ist jetzt voll. + TEST_ASSERT_EQUAL_UINT8(MAX_TOTAL_CONTENT_LENGTH, + mr.index); // Der Puffer ist genau gefüllt. + TEST_ASSERT_EQUAL_UINT8(InPayload, mr.state); // Noch im Payload-Zustand. + + // Das nächste Byte (das letzte Byte von oversized_data_for_buffer) + // wird den Overflow auslösen, da mr->index dann mr->max_total_content_length + // überschreitet. + parse_byte(&mr, oversized_data_for_buffer[MAX_TOTAL_CONTENT_LENGTH]); + + TEST_ASSERT_FALSE(message_received_flag); + TEST_ASSERT_TRUE(message_fail_flag); + TEST_ASSERT_EQUAL_UINT8(msgid, received_msgid); // MSGID ist korrekt gesetzt + // received_payload_len sollte die max. Puffergröße sein, bis der Fehler + // auftrat + TEST_ASSERT_EQUAL_UINT8(MAX_TOTAL_CONTENT_LENGTH, received_payload_len); + TEST_ASSERT_EQUAL_UINT8(MessageToLong, received_error); + TEST_ASSERT_EQUAL_UINT8(WaitingForStartByte, mr.state); + TEST_ASSERT_EQUAL_UINT8(MessageToLong, mr.error); +} + +// Test 8: Unerwartetes StartByte mitten im Frame +void test_8_unexpected_start_byte_in_payload(void) { + struct MessageReceive mr = InitMessageReceive(); + + uint8_t msgid = 0x08; + + uint8_t full_message[] = { + StartByte, msgid, + 0x10, // Teil des Payloads + StartByte, // Unerwartetes StartByte mitten im Payload + 0x20, // Dies würde danach kommen + 0x00, // Dummy-Checksumme + EndByte // Dummy-EndByte }; - uint8_t msg[] = { - 0xAA, 0x02, 0x02, - 0x10, 0x20, 0x00}; // falsche Checksumme (korr. wäre 0x10 ^ 0x20 ^ 2 ^ 2) + parse_byte(&mr, full_message[0]); // StartByte + parse_byte(&mr, full_message[1]); // MSGID + parse_byte(&mr, full_message[2]); // Payload 0x10 - for (uint8_t i = 0; i < sizeof(msg); ++i) - parse_byte(&mr, msg[i]); + // Hier kommt das unerwartete StartByte, sollte den Fehler auslösen und + // resetten + parse_byte(&mr, full_message[3]); + TEST_ASSERT_FALSE(message_received_flag); + TEST_ASSERT_TRUE(message_fail_flag); + TEST_ASSERT_EQUAL_UINT8(msgid, received_msgid); + // received_payload_len sollte die Länge der Daten sein, die vor dem Fehler + // empfangen wurden. + TEST_ASSERT_EQUAL_UINT8(1, received_payload_len); // Nur 0x10 empfangen + TEST_ASSERT_EQUAL_UINT8_ARRAY(((uint8_t[]){0x10}), received_payload, 1); + TEST_ASSERT_EQUAL_UINT8(UnexpectedCommandByte, received_error); TEST_ASSERT_EQUAL_UINT8(WaitingForStartByte, mr.state); + TEST_ASSERT_EQUAL_UINT8(UnexpectedCommandByte, mr.error); } -// Kein Startbyte – Nachricht ignorieren -void test_no_startbyte_ignored(void) { - struct MessageRecieve mr = { - .state = WaitingForStartByte, .index = 0, .checksum = 0}; +// Test 9: Unerwartetes EndByte an der Position der MSGID +void test_9_unexpected_end_byte_at_msgid(void) { + struct MessageReceive mr = InitMessageReceive(); - uint8_t msg[] = {2, 0x10, 0x20, 0x30}; // kein Startbyte + uint8_t full_message[] = {StartByte, EndByte, // EndByte anstelle von MSGID + 0x01, 0x02, 0x03, 0x04, EndByte}; - for (uint8_t i = 0; i < sizeof(msg); ++i) - parse_byte(&mr, msg[i]); + parse_byte(&mr, full_message[0]); // StartByte + parse_byte(&mr, full_message[1]); // EndByte anstelle MSGID + TEST_ASSERT_FALSE(message_received_flag); + TEST_ASSERT_TRUE(message_fail_flag); + TEST_ASSERT_EQUAL_UINT8( + 0x00, received_msgid); // MSGID ist noch 0, da keine empfangen + TEST_ASSERT_EQUAL_UINT8(0, received_payload_len); // Noch kein Payload + TEST_ASSERT_EQUAL_UINT8(UnexpectedCommandByte, received_error); TEST_ASSERT_EQUAL_UINT8(WaitingForStartByte, mr.state); - TEST_ASSERT_EQUAL_UINT8(0, mr.index); + TEST_ASSERT_EQUAL_UINT8(UnexpectedCommandByte, mr.error); } -// Länge 0 – gültige Nachricht ohne Payload -void test_zero_length_message(void) { - struct MessageRecieve mr = { - .state = WaitingForStartByte, .index = 0, .checksum = 0}; +// Test 10: Kein StartByte zu Beginn der Sequenz (Parser sollte ignorieren) +void test_10_no_startbyte_at_beginning_ignored(void) { + struct MessageReceive mr = InitMessageReceive(); - uint8_t msg[] = {0xAA, 3, 0, 0}; // Länge = 0, Checksumme = 3 ^ 0 = 3 - msg[3] = msg[1] ^ msg[2]; + uint8_t msg[] = {0x01, 0x02, 0x03, 0x04}; // Beginnt nicht mit StartByte - for (uint8_t i = 0; i < sizeof(msg); ++i) + for (uint8_t i = 0; i < sizeof(msg); ++i) { parse_byte(&mr, msg[i]); + } - TEST_ASSERT_EQUAL_UINT8(0, mr.index); - TEST_ASSERT_EQUAL_UINT8(3, mr.messageid); + TEST_ASSERT_FALSE(message_received_flag); + TEST_ASSERT_FALSE( + message_fail_flag); // Sollte keinen Fehler melden, nur ignorieren + TEST_ASSERT_EQUAL_UINT8(WaitingForStartByte, + mr.state); // Sollte im Wartezustand bleiben + TEST_ASSERT_EQUAL_UINT8(0, mr.index); // Index sollte 0 bleiben + TEST_ASSERT_EQUAL_UINT8(NoError, mr.error); // Kein Fehler gemeldet +} + +// Test 11: Ungültige Länge (z.B. EndByte kommt zu früh, ohne Checksumme) +// Angenommen, das Protokoll erwartet immer mindestens MSGID + Checksumme. +// Ein leeres Payload ist OK (MSGID + Checksumme + EndByte), aber nur MSGID + +// EndByte ist Fehler. +void test_11_frame_too_short_no_checksum(void) { + struct MessageReceive mr = InitMessageReceive(); + + uint8_t msgid = 0x09; + uint8_t full_message[] = { + StartByte, msgid, EndByte // Kein Payload, keine Checksumme + }; + + for (uint8_t i = 0; i < sizeof(full_message); ++i) { + parse_byte(&mr, full_message[i]); + } + + TEST_ASSERT_FALSE(message_received_flag); + TEST_ASSERT_TRUE(message_fail_flag); + TEST_ASSERT_EQUAL_UINT8(msgid, received_msgid); // MSGID ist bekannt + TEST_ASSERT_EQUAL_UINT8(0, received_payload_len); // Payload-Puffer ist leer + TEST_ASSERT_EQUAL_UINT8(WrongCheckSum, + received_error); // Checksumme kann nicht 0x00 sein TEST_ASSERT_EQUAL_UINT8(WaitingForStartByte, mr.state); + TEST_ASSERT_EQUAL_UINT8(WrongCheckSum, mr.error); } int main(void) { UNITY_BEGIN(); - RUN_TEST(test_valid_message_parses_correctly); - RUN_TEST(test_invalid_checksum_resets_state); - RUN_TEST(test_no_startbyte_ignored); - RUN_TEST(test_zero_length_message); + RUN_TEST(test_1_valid_message_parses_correctly); + RUN_TEST(test_2_invalid_checksum_resets_state); + RUN_TEST(test_3_zero_length_message); + RUN_TEST(test_4_escaped_message_id); + RUN_TEST(test_5_escaped_payload_byte); + RUN_TEST(test_6_escaped_checksum_byte); + RUN_TEST(test_7_message_too_long); + RUN_TEST(test_8_unexpected_start_byte_in_payload); + RUN_TEST(test_9_unexpected_end_byte_at_msgid); + RUN_TEST(test_10_no_startbyte_at_beginning_ignored); + RUN_TEST(test_11_frame_too_short_no_checksum); return UNITY_END(); }