diff --git a/src/Makefile b/src/Makefile index 28907a36..0edaa81b 100644 --- a/src/Makefile +++ b/src/Makefile @@ -81,7 +81,9 @@ export OBJS := \ ../src/drivers/photodiode/photodiode_driver.o \ ../src/drivers/rtc/rtc_driver.o \ ../src/drivers/watchdog/watchdog_driver.o \ -../src/drivers/at86rf215/at86rf215.o \ +../src/drivers/at86rf215/at86rf215.o \ + \ +../src/libraries/cmd_dispatcher/cmd_dispatcher.o \ \ ../src/tasks/heartbeat/heartbeat_main.o \ \ @@ -101,9 +103,6 @@ export OBJS := \ ../src/tasks/task_manager/task_manager_main.o \ ../src/tasks/task_manager/task_manager_task.o \ \ -../src/tasks/command_dispatcher/command_dispatcher_main.o \ -../src/tasks/command_dispatcher/command_dispatcher_task.o \ - \ ../src/tasks/shell/shell_main.o \ ../src/tasks/shell/shell_helpers.o \ ../src/tasks/shell/shell_commands.o \ @@ -135,6 +134,8 @@ export EXTRA_VPATH := \ ../../src/drivers/rtc \ ../../src/drivers/photodiode \ ../../src/drivers/watchdog \ +../../src/libraries \ +../../src/libraries/cmd_dispatcher \ ../../src/tasks \ ../../src/tasks/watchdog \ ../../src/tasks/heartbeat \ diff --git a/src/globals.h b/src/globals.h index fd23760b..5ed18551 100644 --- a/src/globals.h +++ b/src/globals.h @@ -44,10 +44,12 @@ typedef enum { // Error Responses ERROR_READ_FAILED, ERROR_WRITE_FAILED, + ERROR_BAD_ARGS, ERROR_SPI_TRANSFER_FAILED, ERROR_I2C_FAILED, ERROR_TASK_DISABLED, ERROR_BAD_TARGET, + ERROR_QUEUE_ERROR, ERROR_SANITY_CHECK_FAILED, ERROR_PROCESSING_FAILED, ERROR_NOT_READY, diff --git a/src/libraries/adcs b/src/libraries/adcs new file mode 160000 index 00000000..31dff4f3 --- /dev/null +++ b/src/libraries/adcs @@ -0,0 +1 @@ +Subproject commit 31dff4f379832489d87bfa14a5f2a690ca92dbd6 diff --git a/src/libraries/cmd_dispatcher/cmd_dispatcher.c b/src/libraries/cmd_dispatcher/cmd_dispatcher.c new file mode 100644 index 00000000..eaadb419 --- /dev/null +++ b/src/libraries/cmd_dispatcher/cmd_dispatcher.c @@ -0,0 +1,85 @@ +#include "cmd_dispatcher.h" +#include "globals.h" +#include "queue.h" +#include "stdint.h" + +#define CMD_LOG_SZ 16 // The number of commands to keep in the log buffer for debugging purposes + + +static cmd_record_t cmd_log_buffer[CMD_LOG_SZ]; +static uint8_t log_index = 0; // Index to keep track of where to log the next command in the buffer + +/** + * \fn log_command + * + * \brief Log a command in the command log buffer for telemetry purposes + * \param[in] p_cmd a pointer to the command struct to be logged + */ +void log_command(const command_t *p_cmd) { + + cmd_log_buffer[log_index].target = p_cmd->target; + cmd_log_buffer[log_index].data_type = p_cmd->data_type; + cmd_log_buffer[log_index].operation = p_cmd->operation; + + log_index = (log_index + 1) % CMD_LOG_SZ; // Increment index and wrap around if it exceeds buffer size +} + + +/** + * \fn get_n_logs + * + * \brief Get the last n logged commands from the command log buffer + * + * \param[in] n the number of recent commands to retrieve + * \param[out] logs an array to store the retrieved command logs, must be at least size n + * + * \return status_t, whether retrieval was sucessful + */ +status_t get_n_logs(uint8_t n, cmd_record_t *logs) { + + if (n > CMD_LOG_SZ) { + return ERROR_BAD_ARGS; // Cannot retrieve more logs than the buffer size + } + + for (uint8_t i = 0; i < n; i++) { + logs[i] = cmd_log_buffer[log_index]; + log_index = (log_index == 0) ? (CMD_LOG_SZ - 1) : (log_index - 1); // Move to the previous log, wrap around if necessary + } + + return SUCCESS; +} + + +/** + * \fn dispatch_command + * + * \brief Forward command to the appropriate task for execution and + * log the command for telemetry + * + * \param[in] p_cmd a pointer to the command struct to be dispatched + * + * \return status_t, whether the forwarding was successful or not + */ +status_t enqueue_command(command_t *p_cmd) { + + pvdx_task_t *target = p_cmd->target; + + // Log cmd into buffer for telemetry + log_command(p_cmd); + + // Check if target is valid and enabled + if (target == NULL) { + return ERROR_BAD_TARGET; + } + if (!target->enabled) { + return ERROR_TASK_DISABLED; + } + + // now forward cmd to task command queue + if (xQueueSendToBack(p_cmd->target->command_queue, p_cmd, 0) != pdTRUE) { + return ERROR_QUEUE_ERROR; + } + + return SUCCESS; +} + diff --git a/src/libraries/cmd_dispatcher/cmd_dispatcher.h b/src/libraries/cmd_dispatcher/cmd_dispatcher.h new file mode 100644 index 00000000..86b85c63 --- /dev/null +++ b/src/libraries/cmd_dispatcher/cmd_dispatcher.h @@ -0,0 +1,15 @@ +#ifndef CMD_DISPATCHER_H +#define CMD_DISPATCHER_H + +#include "globals.h" + +typedef struct { + pvdx_task_t *target; + command_data_type_t data_type; + operation_t operation; +} cmd_record_t; + +status_t get_n_logs(uint8_t n, cmd_record_t *logs); +status_t enqueue_command(command_t *p_cmd); + +#endif // CMD_DISPATCHER_H \ No newline at end of file diff --git a/src/tasks/adcs/adcs_main.c b/src/tasks/adcs/adcs_main.c index 5f40a245..d1ab9190 100644 --- a/src/tasks/adcs/adcs_main.c +++ b/src/tasks/adcs/adcs_main.c @@ -9,11 +9,10 @@ */ #include "adcs_task.h" -#include "command_dispatcher_task.h" #include "globals.h" #include "logging.h" #include "watchdog_task.h" - +#include "cmd_dispatcher.h" // ADCS Task memory structures adcs_task_memory_t adcs_mem; @@ -106,7 +105,9 @@ void main_adcs(void *pvParameters) { // Check in with the watchdog task if (should_checkin(current_task)) { - enqueue_command(&cmd_checkin); + while (enqueue_command(&cmd_checkin) != SUCCESS) { + warning("adcs: Failed to enqueue watchdog checkin command\n"); + } debug("adcs: Enqueued watchdog checkin command\n"); } } diff --git a/src/tasks/command_dispatcher/command_dispatcher_main.c b/src/tasks/command_dispatcher/command_dispatcher_main.c deleted file mode 100644 index dbc3a9fa..00000000 --- a/src/tasks/command_dispatcher/command_dispatcher_main.c +++ /dev/null @@ -1,60 +0,0 @@ -/** - * command_dispatcher_main.c - * - * Main loop of the Command Dispatcher task. This task is responsible for receiving commands - * from other tasks and forwarding them to the appropriate task for execution. All major - * commands MUST be sent through the Command Dispatcher task to enable consistent logging and - * adhere to the PVDXos hub-and-spoke architecture. - * - * Created: October 13, 2024 - * Authors: Tanish Makadia, Yi Liu, Siddharta Laloux - */ - -#include "command_dispatcher_task.h" -#include "watchdog_task.h" - -command_dispatcher_task_memory_t command_dispatcher_mem; - -/** - * \fn main_command_dispatcher - * - * \param pvParameters a void pointer to the parametres required by the command - * dispatcher; not currently set by config - * - * \warning should never return - */ -void main_command_dispatcher(void *pvParameters) { - info("command_dispatcher: Task Started!\n"); - - // Obtain a pointer to the current task within the global task list - pvdx_task_t *const current_task = get_current_task(); - // Cache the watchdog checkin command to avoid creating it every iteration - command_t cmd_checkin = get_watchdog_checkin_command(current_task); - // Calculate the maximum time the command dispatcher should block (and thus be unable to check in with the watchdog) - const TickType_t queue_block_time_ticks = get_command_queue_block_time_ticks(current_task); - // Varible to hold commands popped off the queue - command_t cmd; - - while (true) { - debug("\n---------- Command Dispatcher Task Loop ----------\n"); - - // Block waiting for at least one command to appear in the command queue - if (xQueueReceive(p_command_dispatcher_task->command_queue, &cmd, queue_block_time_ticks) == pdPASS) { - // Once there is at least one command in the queue, empty the entire queue - do { - debug("command_dispatcher: Command popped off queue. Target: %d, Operation: %d\n", cmd.target, cmd.operation); - dispatch_command(&cmd); - } while (xQueueReceive(p_command_dispatcher_task->command_queue, &cmd, 0) == pdPASS); - } - debug("command_dispatcher: No more commands queued.\n"); - - // Check in with the watchdog task - if (should_checkin(current_task)) { - enqueue_command(&cmd_checkin); - debug("command_dispatcher: Enqueued watchdog checkin command\n"); - } - - // Guarantee other tasks get time to run - vTaskDelay(pdMS_TO_TICKS(100)); - } -} diff --git a/src/tasks/command_dispatcher/command_dispatcher_task.c b/src/tasks/command_dispatcher/command_dispatcher_task.c deleted file mode 100644 index 4dbbe115..00000000 --- a/src/tasks/command_dispatcher/command_dispatcher_task.c +++ /dev/null @@ -1,90 +0,0 @@ -/** - * command_dispatcher_task.c - * - * Helper functions for the Command Dispatcher task. This task is responsible for receiving - * commands from other tasks and forwarding them to the appropriate task for execution. All major - * commands MUST be sent through the Command Dispatcher task to enable consistent logging and adhere - * to the PVDXos hub-and-spoke architecture. - * - * Created: October 13, 2024 - * Authors: Tanish Makadia, Yi Liu, Siddharta Laloux, Ignacio Blancas Rodriguez - */ - -#include "command_dispatcher_task.h" - -#include "task_list.h" - -/* ---------- DISPATCHABLE FUNCTIONS (sent as commands through the command dispatcher task) ---------- */ - -// NOTE: No dispatchable functions for the command dispatcher task. Its sole purpose is to -// forward commands to other tasks. Essentially, it's a glorified queue. - -/* ---------- NON-DISPATCHABLE FUNCTIONS (do not go through the command dispatcher) ---------- */ - -/** - * \fn init_commamd_dispatecher - * - * \brief Initializes command dispatcher queue, before `init_task_pointer()`. - * - * \returns QueueHandle_t, a handle to the created queue - * - * \see `init_task_pointer()` for usage of functions of the type `init_()` - */ -QueueHandle_t init_command_dispatcher(void) { - QueueHandle_t command_dispatcher_command_queue_handle = xQueueCreateStatic( - COMMAND_QUEUE_MAX_COMMANDS, COMMAND_QUEUE_ITEM_SIZE, command_dispatcher_mem.command_dispatcher_command_queue_buffer, - &command_dispatcher_mem.command_dispatcher_task_queue); - - if (command_dispatcher_command_queue_handle == NULL) { - fatal("Failed to create command queue!\n"); - } - - return command_dispatcher_command_queue_handle; -} - -/** - * \fn enqueue_command - * - * \brief Enqueue a command to be forwarded by the command dispatcher - * - * \param p_cmd a pointer to the command struct to be enqueued - */ -void enqueue_command(command_t *const p_cmd) { - if (xQueueSendToBack(p_command_dispatcher_task->command_queue, p_cmd, 0) != pdTRUE) { - fatal("%s task failed to enqueue command onto Command Dispatcher queue!\n", get_current_task()->name); - } -} - -/** - * \fn dispatch_command - * - * \brief Forward a dequeued command to the appropriate task for execution - * - * \param p_cmd a pointer to the command struct to be dispatched - * - * \return status_t, whether the forwarding was successful or not - * - * \warning produces `ERROR_BAD_TARGET` if target null - * \warning produces `ERROR_TASK_DISABLED` if target disabled - * \warning `fatal` error if command cannot be forwarded to queue - */ -status_t dispatch_command(command_t *const p_cmd) { - // Check if the task is non-NULL - // TODO: make this check exhaustive - if (p_cmd->target == NULL) { - return ERROR_BAD_TARGET; - } - - // Check if the task to dispatch to was disabled - if (!p_cmd->target->enabled) { - return ERROR_TASK_DISABLED; - } - - if (xQueueSendToBack(p_cmd->target->command_queue, p_cmd, 0) != pdTRUE) { - fatal("command-dispatcher: Failed to forward command to %s task\n", p_cmd->target->name); - } - - debug("command-dispatcher: Forwarded a command to %s task\n", p_cmd->target->name); - - return SUCCESS; -} diff --git a/src/tasks/command_dispatcher/command_dispatcher_task.h b/src/tasks/command_dispatcher/command_dispatcher_task.h deleted file mode 100644 index 7b96988c..00000000 --- a/src/tasks/command_dispatcher/command_dispatcher_task.h +++ /dev/null @@ -1,30 +0,0 @@ -#ifndef COMMAND_DISPATCHER_H -#define COMMAND_DISPATCHER_H - -// Includes -#include "globals.h" -#include "logging.h" -#include "queue.h" -#include "task_list.h" - -// Constants -#define COMMAND_DISPATCHER_TASK_STACK_SIZE 1024 // Size of the stack in words (multiply by 4 to get bytes) - -// Placed in a struct to ensure that the TCB is placed higher than the stack in memory -//^ This ensures that stack overflows do not corrupt the TCB (since the stack grows downwards) -typedef struct { - StackType_t overflow_buffer[TASK_STACK_OVERFLOW_PADDING]; - StackType_t command_dispatcher_task_stack[COMMAND_DISPATCHER_TASK_STACK_SIZE]; - uint8_t command_dispatcher_command_queue_buffer[COMMAND_QUEUE_MAX_COMMANDS * COMMAND_QUEUE_ITEM_SIZE]; - StaticQueue_t command_dispatcher_task_queue; - StaticTask_t command_dispatcher_task_tcb; -} command_dispatcher_task_memory_t; - -extern command_dispatcher_task_memory_t command_dispatcher_mem; - -QueueHandle_t init_command_dispatcher(void); -void main_command_dispatcher(void *pvParameters); -status_t dispatch_command(command_t *const p_cmd); -void enqueue_command(command_t *const p_cmd); - -#endif // COMMAND_DISPATCHER_H diff --git a/src/tasks/display/display_main.c b/src/tasks/display/display_main.c index b158a028..ac7b1f7c 100644 --- a/src/tasks/display/display_main.c +++ b/src/tasks/display/display_main.c @@ -7,8 +7,8 @@ * Authors: Tanish Makadia, Ignacio Blancas Rodriguez, Siddharta Laloux */ -#include "command_dispatcher_task.h" #include "display_task.h" +#include "cmd_dispatcher.h" // Display Task memory structures display_task_memory_t display_mem; @@ -58,7 +58,9 @@ void main_display(void *pvParameters) { { // TODO: Add logic for blocking on the result of the display_image command command_t display_image_command = get_display_image_command(IMAGE_BUFFER_PVDX); - enqueue_command(&display_image_command); + while (enqueue_command(&display_image_command) != SUCCESS) { + warning("display: Failed to enqueue display image command\n"); + } if (display_image_command.result != SUCCESS) { warning("display: Failed to display image. Error code: %d\n", display_image_command.result); @@ -68,7 +70,9 @@ void main_display(void *pvParameters) { // Set the display buffer to the second image // TODO: Add logic for blocking on the result of the display_image command command_t display_image_command = get_display_image_command(IMAGE_BUFFER_BROWNLOGO); - enqueue_command(&display_image_command); + while (enqueue_command(&display_image_command) != SUCCESS) { + warning("display: Failed to enqueue display image command\n"); + } if (display_image_command.result != SUCCESS) { warning("display: Failed to display image. Error code: %d\n", display_image_command.result); @@ -77,7 +81,10 @@ void main_display(void *pvParameters) { // Check in with the watchdog task if (should_checkin(current_task)) { - enqueue_command(&cmd_checkin); + while (enqueue_command(&cmd_checkin) != SUCCESS) { + warning("display: Failed to enqueue watchdog checkin command\n"); + } + debug("display: Enqueued watchdog checkin command\n"); } } diff --git a/src/tasks/heartbeat/heartbeat_main.c b/src/tasks/heartbeat/heartbeat_main.c index fd563838..2e70ac5d 100644 --- a/src/tasks/heartbeat/heartbeat_main.c +++ b/src/tasks/heartbeat/heartbeat_main.c @@ -9,8 +9,8 @@ */ #include "logging.h" -#include "tasks/command_dispatcher/command_dispatcher_task.h" -#include "tasks/heartbeat/heartbeat_task.h" +#include "heartbeat_task.h" +#include "cmd_dispatcher.h" heartbeat_task_memory_t heartbeat_mem ; @@ -81,5 +81,7 @@ void main_heartbeat(void *pvParameters) // Check in with the watchdog task if (should_checkin(current_task)) { - enqueue_command(&cmd_checkin) ; + while (enqueue_command(&cmd_checkin) != SUCCESS) { + warning("heartbeat: Failed to enqueue watchdog checkin command\n"); + } debug("heartbeat: Enqueued watchdog checkin command\n") ;}}} diff --git a/src/tasks/shell/shell_commands.c b/src/tasks/shell/shell_commands.c index cf7ba854..f008dbf3 100644 --- a/src/tasks/shell/shell_commands.c +++ b/src/tasks/shell/shell_commands.c @@ -10,14 +10,13 @@ #include "shell_commands.h" #include - -#include "command_dispatcher_task.h" #include "display_task.h" #include "image_buffers/image_buffer_BrownLogo.h" #include "image_buffers/image_buffer_PVDX.h" #include "logging.h" #include "shell_helpers.h" #include "watchdog_task.h" +#include "cmd_dispatcher.h" shell_command_t shell_commands[] = { {"help", shell_help, help_help}, {"echo", shell_echo, help_echo}, @@ -248,7 +247,10 @@ void shell_display(char **args, int arg_count) { const uint8_t *image_buffers[] = {IMAGE_BUFFER_BROWNLOGO, IMAGE_BUFFER_PVDX}; command_t display_image_command = get_display_image_command(image_buffers[args[1][0] - '0']); - enqueue_command(&display_image_command); + + while (enqueue_command(&display_image_command) != SUCCESS) { + warning("shell: Failed to enqueue display image command\n"); + } terminal_printf("fr\n"); } diff --git a/src/tasks/shell/shell_helpers.c b/src/tasks/shell/shell_helpers.c index 706ac879..6f420666 100644 --- a/src/tasks/shell/shell_helpers.c +++ b/src/tasks/shell/shell_helpers.c @@ -9,8 +9,7 @@ */ #include "shell_helpers.h" - -#include "command_dispatcher/command_dispatcher_task.h" +#include "cmd_dispatcher.h" /* ---------- DISPATCHABLE FUNCTIONS (sent as commands through the command dispatcher task) ---------- */ @@ -28,7 +27,9 @@ size_t get_line_from_terminal(uint8_t *p_linebuffer) { while (true) { // This is really the loop that we expect the program to spend most of its time in, so pet the watchdog here - enqueue_command(&cmd_checkin); + while (enqueue_command(&cmd_checkin) != SUCCESS) { + warning("shell: Failed to enqueue watchdog checkin command\n"); + } debug("shell: Enqueued watchdog checkin command\n"); int character_read = SEGGER_RTT_GetKey(); diff --git a/src/tasks/task_list.c b/src/tasks/task_list.c index acc8fe23..1571564f 100644 --- a/src/tasks/task_list.c +++ b/src/tasks/task_list.c @@ -10,7 +10,6 @@ #include "task_list.h" -#include "command_dispatcher_task.h" #include "display_task.h" #include "globals.h" #include "heartbeat_task.h" @@ -36,22 +35,6 @@ pvdx_task_t watchdog_task = {.name = "Watchdog", .has_registered = false, .task_type = OS}; -pvdx_task_t command_dispatcher_task = {.name = "CommandDispatcher", - .enabled = true, - .handle = NULL, - .command_queue = NULL, - .init = init_command_dispatcher, - .function = main_command_dispatcher, - .stack_size = COMMAND_DISPATCHER_TASK_STACK_SIZE, - .stack_buffer = command_dispatcher_mem.command_dispatcher_task_stack, - .pvParameters = NULL, - .priority = 4, - .task_tcb = &command_dispatcher_mem.command_dispatcher_task_tcb, - .watchdog_timeout_ms = 10000, - .last_checkin_time_ticks = 0xDEADBEEF, - .has_registered = false, - .task_type = OS}; - pvdx_task_t task_manager_task = {.name = "TaskManager", .enabled = true, .handle = NULL, @@ -136,7 +119,6 @@ pvdx_task_t heartbeat_task = { // and define their constant pointers pvdx_task_t *const p_watchdog_task = &watchdog_task; -pvdx_task_t *const p_command_dispatcher_task = &command_dispatcher_task; pvdx_task_t *const p_task_manager_task = &task_manager_task; pvdx_task_t *const p_adcs_task = &adcs_task; pvdx_task_t *const p_shell_task = &shell_task; @@ -151,7 +133,7 @@ pvdx_task_t *const task_list_null_terminator = NULL; // NOTE: Watchdog task must be first in the list, Command Dispatcher second, and Task Manager third. // If you change the order of any of these, make sure that main.c reflects the change and update this comment. pvdx_task_t *task_list[] = { - p_watchdog_task, p_command_dispatcher_task, p_task_manager_task, p_adcs_task, p_shell_task, + p_watchdog_task, p_task_manager_task, p_adcs_task, p_shell_task, p_display_task, p_heartbeat_task, task_list_null_terminator, }; diff --git a/src/tasks/task_manager/task_manager_main.c b/src/tasks/task_manager/task_manager_main.c index c7e8f691..cf8f3b34 100644 --- a/src/tasks/task_manager/task_manager_main.c +++ b/src/tasks/task_manager/task_manager_main.c @@ -9,9 +9,9 @@ * Defne Doken, Aidan Wang, Jai Garg, Alex Khosrowshahi, Zach Mahan */ -#include "command_dispatcher_task.h" #include "task_manager_task.h" #include "watchdog_task.h" +#include "cmd_dispatcher.h" task_manager_task_memory_t task_manager_mem; SemaphoreHandle_t task_list_mutex = NULL; @@ -53,7 +53,9 @@ void main_task_manager(void *pvParameters) { command_t *const p_initialise_all_tasks = &initialise_all_tasks; - enqueue_command(p_initialise_all_tasks); + while (enqueue_command(p_initialise_all_tasks) != SUCCESS) { + warning("task_manager: Failed to task initialise command\n"); + } while (true) { debug("\n---------- Task Manager Task Loop ----------\n"); @@ -69,7 +71,9 @@ void main_task_manager(void *pvParameters) { // Check in with the watchdog task if (should_checkin(current_task)) { - enqueue_command(&cmd_checkin); + while (enqueue_command(&cmd_checkin) != SUCCESS) { + warning("task_manager: Failed to enqueue watchdog checkin command\n"); + } debug("task_manager: Enqueued watchdog checkin command\n"); } } diff --git a/src/tasks/watchdog/watchdog_main.c b/src/tasks/watchdog/watchdog_main.c index af0be42b..72593aee 100644 --- a/src/tasks/watchdog/watchdog_main.c +++ b/src/tasks/watchdog/watchdog_main.c @@ -8,8 +8,8 @@ * Authors: Oren Kohavi, Tanish Makadia, Siddharta Laloux */ -#include "tasks/command_dispatcher/command_dispatcher_task.h" #include "watchdog_task.h" +#include "cmd_dispatcher.h" watchdog_task_memory_t watchdog_mem; QueueHandle_t watchdog_command_queue_handle; @@ -72,7 +72,9 @@ void main_watchdog(void *pvParameters) { // Watchdog Task must also check-in with itself if (should_checkin(current_task)) { - enqueue_command(&cmd_checkin); + while (enqueue_command(&cmd_checkin) != SUCCESS) { + warning("watchdog: Failed to enqueue watchdog checkin command\n"); + } debug("watchdog: Enqueued watchdog checkin command\n"); }