refactoring files
Dieser Commit ist enthalten in:
1 Ursprung
58dcdbdcb3
Commit
ab51ed746a
15 Dateien geändert
+945
-292
Keine Dateien entsprechen Ihrer Suche
+165
-48
@@ -1,6 +1,17 @@
|
||||
/**
|
||||
* @file cli.cpp
|
||||
* @brief Interactive asynchronous Command Line Interface implementation for FoxMesh
|
||||
* @author OM Tom (OE3TKT)
|
||||
*
|
||||
* @license SPDX-License-Identifier: MIT
|
||||
* @copyright Copyright (c) 2026 OM Tom (OE3TKT)
|
||||
* For the full license terms, see the LICENSE file in the project root.
|
||||
*/
|
||||
|
||||
#include "cli.hpp"
|
||||
#include <cstring>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/timers.h"
|
||||
@@ -9,36 +20,69 @@
|
||||
#include "config.hpp"
|
||||
|
||||
static const char* TAG = "CLI";
|
||||
static i2c* i2c_bus = nullptr;
|
||||
static LedController* led_system = nullptr;
|
||||
static DisplayController* oled_display = nullptr;
|
||||
|
||||
#define CLI_UART_PORT UART_NUM_0
|
||||
#define BUF_SIZE 1024
|
||||
|
||||
// Handle a silence timer for suppressing logs during cli actions
|
||||
/**
|
||||
* @brief Handle a silence timer for suppressing logs during interactive CLI actions.
|
||||
*/
|
||||
static TimerHandle_t silence_timer = nullptr;
|
||||
// Dynamic variable holding the current mute runtime
|
||||
|
||||
/**
|
||||
* @brief Dynamic configuration profile holding the current logging suppression runtime in seconds.
|
||||
*/
|
||||
static uint32_t current_mute_time_sec = Config::DEFAULT_MUTE_TIME_SEC;
|
||||
|
||||
/**
|
||||
* @brief Initializes the internal pointers to project singletons.
|
||||
* @param bus_pointer Active I2C master bus context.
|
||||
* @param led_pointer Active status LED task instance.
|
||||
* @param display_pointer Active OLED worker instance.
|
||||
*/
|
||||
void cli_init(i2c* bus_pointer, LedController* led_pointer, DisplayController* display_pointer) {
|
||||
i2c_bus = bus_pointer;
|
||||
led_system = led_pointer;
|
||||
oled_display = display_pointer;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Software timer callback task executed once terminal interaction has gone stale.
|
||||
* @param xTimer Handle tracking the specific FreeRTOS timing token instance.
|
||||
*/
|
||||
static void log_restore_timer_callback(TimerHandle_t xTimer) {
|
||||
// Print a clean new prompt since logs might have disrupted the visual flow
|
||||
(void)xTimer; // Prevent compiler warning about unused parameter
|
||||
|
||||
// Print a clean new prompt since logs might have disrupted the visual terminal interface
|
||||
printf("\r\n[System] Debug logs unmuted after %lu seconds of inactivity. \r\n%s #\r\n",
|
||||
(unsigned long)current_mute_time_sec, Config::PROJECT_TITLE);
|
||||
fflush(stdout);
|
||||
|
||||
// Now safely restore global logging back to INFO level after inactivity
|
||||
// Securely restore native logging profiles back to application default INFO metrics
|
||||
esp_log_level_set("*", ESP_LOG_INFO);
|
||||
}
|
||||
|
||||
// Process input strings once the user presses Enter
|
||||
static void process_command(const char* cmd) {
|
||||
/**
|
||||
* @brief Tokenizes and evaluates commands received over the serial UART layer.
|
||||
* @param cmd Raw null-terminated character array.
|
||||
*/
|
||||
void process_command(const char* cmd) {
|
||||
if (strlen(cmd) == 0) return;
|
||||
|
||||
printf("\r\n"); // Move to new line after command execution
|
||||
printf("\r\n");
|
||||
|
||||
if (strcmp(cmd, "help") == 0) {
|
||||
printf("Available commands:\r\n");
|
||||
printf(" help - Show this command overview\r\n");
|
||||
printf(" version - Display project title and version\r\n");
|
||||
printf(" status - Print internal hardware statistics\r\n");
|
||||
printf(" mute <sec> - Set custom logging timeout (Current: %lus)\r\n", (unsigned long)current_mute_time_sec);
|
||||
printf(" help - Show this command overview\r\n");
|
||||
printf(" version - Display project title and version\r\n");
|
||||
printf(" status - Print internal hardware statistics\r\n");
|
||||
printf(" mute <sec> - Set custom logging timeout (Current: %lus)\r\n", (unsigned long)current_mute_time_sec);
|
||||
printf(" blink <int> <puls> - Set LED profile in ms (e.g. blink 1000 50)\r\n");
|
||||
printf(" oled <line> <txt> - Print text to OLED line 0-7 (e.g. oled 0 Test)\r\n");
|
||||
printf(" i2c - Scan the configured I2C bus infrastructure\r\n");
|
||||
}
|
||||
else if (strcmp(cmd, "version") == 0) {
|
||||
printf("%s System - Running Version: %s\r\n", Config::PROJECT_TITLE, Config::PROJECT_VERSION);
|
||||
@@ -48,35 +92,105 @@ static void process_command(const char* cmd) {
|
||||
printf("Free Internal Heap: %lu bytes\r\n", (unsigned long)esp_get_free_heap_size());
|
||||
printf("Mute Timer Period : %lu seconds\r\n", (unsigned long)current_mute_time_sec);
|
||||
printf("Target CPU Board : ESP32\r\n");
|
||||
printf("ESP-IDF Version : %s\r\n", esp_get_idf_version());
|
||||
}
|
||||
// NEW: Handle the "mute" parameter runtime configuration
|
||||
else if (strncmp(cmd, "mute ", 5) == 0) {
|
||||
// Extract the value after the string space
|
||||
int new_time = atoi(cmd + 5);
|
||||
if (new_time > 0 && new_time <= 3600) { // Limit between 1 second and 1 hour
|
||||
current_mute_time_sec = new_time;
|
||||
int parsed_time = atoi(cmd + 5);
|
||||
|
||||
// Instantly update the running FreeRTOS timer period
|
||||
if (silence_timer != nullptr) {
|
||||
xTimerChangePeriod(silence_timer, pdMS_TO_TICKS(current_mute_time_sec * 1000), pdMS_TO_TICKS(10));
|
||||
// FIXED: Expanded structural boundaries to explicitly accept 0 as a infinite lock parameter
|
||||
if (parsed_time >= 0 && parsed_time <= 3600) {
|
||||
current_mute_time_sec = static_cast<uint32_t>(parsed_time);
|
||||
|
||||
if (current_mute_time_sec == 0) {
|
||||
// INFINITE MUTE: Security halt on the active FreeRTOS software timer instance
|
||||
if (silence_timer != nullptr) {
|
||||
xTimerStop(silence_timer, pdMS_TO_TICKS(10));
|
||||
}
|
||||
printf("Success: Infinite mute activated. Debug logs are permanently suppressed.\r\n");
|
||||
}
|
||||
else {
|
||||
// DYNAMIC TIMEOUT: Extracted multiplication step to decouple constant values from structural macros
|
||||
uint32_t timeout_ms = current_mute_time_sec * 1000;
|
||||
|
||||
// Dynamically re-trigger and alter operating parameters on the running software timer
|
||||
if (silence_timer != nullptr) {
|
||||
xTimerChangePeriod(silence_timer, pdMS_TO_TICKS(timeout_ms), pdMS_TO_TICKS(10));
|
||||
}
|
||||
printf("Success: Mute timer interval updated to %lu seconds.\r\n", (unsigned long)current_mute_time_sec);
|
||||
}
|
||||
printf("Success: Mute timer interval updated to %lu seconds.\r\n", (unsigned long)current_mute_time_sec);
|
||||
} else {
|
||||
printf("Error: Please provide a valid timeout between 1 and 3600 seconds.\r\n");
|
||||
printf("Error: Please provide a valid timeout between 0 (infinite) and 3600 seconds.\r\n");
|
||||
}
|
||||
}
|
||||
else if (strncmp(cmd, "blink ", 6) == 0) {
|
||||
uint32_t interval = 0;
|
||||
uint32_t pulse = 0;
|
||||
|
||||
// FIXED: Swapped sscanf parsing tokens to %lu to eliminate narrowing warnings on signed integers
|
||||
if (sscanf(cmd + 6, "%lu %lu", &interval, &pulse) == 2) {
|
||||
if (interval > 0 && pulse > 0 && pulse < interval) {
|
||||
if (led_system != nullptr) {
|
||||
LedController::BlinkProfile customProfile;
|
||||
customProfile.intervalMs = interval;
|
||||
customProfile.pulseWidthMs = pulse;
|
||||
|
||||
led_system->updateProfile(customProfile);
|
||||
printf("Success: LED profile updated. Interval: %lu ms, Pulse: %lu ms.\r\n",
|
||||
(unsigned long)interval, (unsigned long)pulse);
|
||||
} else {
|
||||
printf("Error: LedController handle is uninitialized.\r\n");
|
||||
}
|
||||
} else {
|
||||
printf("Error: Invalid boundaries. Pulse width must be smaller than total interval.\r\n");
|
||||
}
|
||||
} else {
|
||||
printf("Usage error: Please specify parameters as 'blink <interval_ms> <pulse_width_ms>'\r\n");
|
||||
}
|
||||
}
|
||||
else if (strncmp(cmd, "oled ", 5) == 0) {
|
||||
uint32_t targetLine = 0;
|
||||
char textBuffer[64];
|
||||
|
||||
// FIXED: Stabilized parsing definitions matching long unsigned integer casting requirements
|
||||
if (sscanf(cmd + 5, "%lu %63[^\r\n]", &targetLine, textBuffer) == 2) {
|
||||
if (targetLine <= 7) {
|
||||
if (oled_display != nullptr) {
|
||||
oled_display->printMessage(textBuffer, static_cast<uint8_t>(targetLine));
|
||||
printf("Success: Dispatched text '%s' to OLED line %lu.\r\n", textBuffer, (unsigned long)targetLine);
|
||||
} else {
|
||||
printf("Error: DisplayController handle is uninitialized.\r\n");
|
||||
}
|
||||
} else {
|
||||
printf("Error: Target line must be within the range 0-7.\r\n");
|
||||
}
|
||||
} else {
|
||||
printf("Usage error: Please specify parameters as 'oled <line_id> <text_message>'\r\n");
|
||||
}
|
||||
}
|
||||
else if (strcmp(cmd, "i2c") == 0) {
|
||||
if (i2c_bus != nullptr) {
|
||||
printf("Scanning I2C Bus... \r\n");
|
||||
i2c_bus->scan();
|
||||
} else {
|
||||
printf("Error: I2C Bus not initialized in CLI!\r\n");
|
||||
}
|
||||
}
|
||||
else {
|
||||
printf("Unknown command: '%s'. Type 'help' for options.\r\n", cmd);
|
||||
}
|
||||
|
||||
// Print command prompt again
|
||||
printf("\r\n%s # ", Config::PROJECT_TITLE);
|
||||
printf("%s # ", Config::PROJECT_TITLE);
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
// Background listening loop for keyboard input
|
||||
/**
|
||||
* @brief Infinite FreeRTOS task handling non-blocking background collection of serial input characters.
|
||||
* @param pvParameters Context input pointer passed by the task scheduler (unused).
|
||||
*/
|
||||
static void cli_worker_task(void *pvParameters) {
|
||||
// Configure UART parameters for native monitoring
|
||||
(void)pvParameters;
|
||||
|
||||
// Configure low-level peripheral settings for direct serial translation
|
||||
uart_config_t uart_config = {};
|
||||
uart_config.baud_rate = 115200;
|
||||
uart_config.data_bits = UART_DATA_8_BITS;
|
||||
@@ -85,75 +199,78 @@ static void cli_worker_task(void *pvParameters) {
|
||||
uart_config.flow_ctrl = UART_HW_FLOWCTRL_DISABLE;
|
||||
uart_config.source_clk = UART_SCLK_DEFAULT;
|
||||
|
||||
// Install UART driver without using resource-heavy interrupt queues
|
||||
// Direct registration of system driver assets bypassing explicit peripheral queue maps
|
||||
ESP_ERROR_CHECK(uart_param_config(CLI_UART_PORT, &uart_config));
|
||||
ESP_ERROR_CHECK(uart_driver_install(CLI_UART_PORT, BUF_SIZE * 2, 0, 0, NULL, 0));
|
||||
ESP_ERROR_CHECK(uart_driver_install(CLI_UART_PORT, BUF_SIZE * 2, 0, 0, nullptr, 0));
|
||||
|
||||
char line_buffer[BUF_SIZE];
|
||||
int buffer_index = 0;
|
||||
|
||||
// Welcome message and initial prompt
|
||||
printf("\r\n=== Interactive %s CLI Console Started ===\r\n", Config::PROJECT_TITLE);
|
||||
printf("Type 'help' to get a list of active instructions.\r\n\r\n");
|
||||
printf("%s # ", Config::PROJECT_TITLE);
|
||||
fflush(stdout);
|
||||
|
||||
uint8_t data;
|
||||
uint8_t data = 0;
|
||||
while (1) {
|
||||
// Read character by character from serial interface (non-blocking with 20ms timeout)
|
||||
// Evaluate input character frames utilizing a non-blocking asynchronous approach
|
||||
int len = uart_read_bytes(CLI_UART_PORT, &data, 1, pdMS_TO_TICKS(20));
|
||||
|
||||
if (len > 0) {
|
||||
// Drop system verbosity parameters globally during active interaction sequence
|
||||
esp_log_level_set("*", ESP_LOG_NONE);
|
||||
|
||||
if (silence_timer != nullptr) {
|
||||
// FIXED: Only reset the timer if we are not in permanent infinite mute mode (current_mute_time_sec != 0)
|
||||
if (silence_timer != nullptr && current_mute_time_sec != 0) {
|
||||
xTimerReset(silence_timer, pdMS_TO_TICKS(10));
|
||||
}
|
||||
// Handle carriage return or line feed (Enter key pressed)
|
||||
|
||||
// Capture Carriage Return or Line Feed sequence boundaries
|
||||
if (data == '\r' || data == '\n') {
|
||||
line_buffer[buffer_index] = '\0'; // Null-terminate string
|
||||
line_buffer[buffer_index] = '\0';
|
||||
process_command(line_buffer);
|
||||
buffer_index = 0; // Reset buffer index for next command
|
||||
buffer_index = 0;
|
||||
}
|
||||
// Handle Backspace (ASCII 8 or 127)
|
||||
// Evaluate Backspace command frames (ASCII 8 or Delete 127 symbols)
|
||||
else if ((data == 8 || data == 127) && buffer_index > 0) {
|
||||
buffer_index--;
|
||||
printf("\b \b"); // Clear character visually in user terminal
|
||||
printf("\b \b");
|
||||
fflush(stdout);
|
||||
}
|
||||
// Add normal printable characters to buffer
|
||||
// Sanitize and append plain terminal symbols into processing context
|
||||
else if (data >= 32 && data <= 126 && buffer_index < (BUF_SIZE - 1)) {
|
||||
line_buffer[buffer_index++] = data;
|
||||
printf("%c", data); // Terminal Echo (prints what user types)
|
||||
line_buffer[buffer_index++] = static_cast<char>(data);
|
||||
printf("%c", data);
|
||||
fflush(stdout);
|
||||
}
|
||||
}
|
||||
// Minimal delay to keep watchdog and FreeRTOS internal background tasks happy
|
||||
// Structural task yields preventing internal stack watchdog violations
|
||||
vTaskDelay(pdMS_TO_TICKS(10));
|
||||
}
|
||||
}
|
||||
|
||||
void start_cli_task(void) {
|
||||
// Dynamically initialize the timer with the variable interval using milliseconds conversion
|
||||
// FIXED: Pre-calculated dynamic period duration variable ensuring safe macro consumption
|
||||
uint32_t silence_period_ms = current_mute_time_sec * 1000;
|
||||
|
||||
silence_timer = xTimerCreate(
|
||||
"MuteTimer",
|
||||
pdMS_TO_TICKS(current_mute_time_sec * 1000),
|
||||
pdMS_TO_TICKS(silence_period_ms),
|
||||
pdFALSE,
|
||||
nullptr,
|
||||
log_restore_timer_callback
|
||||
);
|
||||
|
||||
if (silence_timer == nullptr) {
|
||||
ESP_LOGE(TAG, "Failed to create FreeRTOS silence timer.");
|
||||
}
|
||||
|
||||
// Allocate resources on Core 0 to decouple console loads from LoRa packet handling on Core 1
|
||||
xTaskCreatePinnedToCore(
|
||||
cli_worker_task,
|
||||
"CLI_InterfaceTask",
|
||||
4096, // Slightly bigger stack to safely handle printf string buffers
|
||||
NULL,
|
||||
4096,
|
||||
nullptr,
|
||||
5,
|
||||
NULL,
|
||||
0 // Hosted on Core 0 to separate terminal interactive traffic from Core 1
|
||||
nullptr,
|
||||
0
|
||||
);
|
||||
}
|
||||
In neuem Issue referenzieren
Einen Benutzer sperren