LCOV - code coverage report
Current view: top level - App/Src - appLogger.cpp (source / functions) Coverage Total Hit
Test: filtered_coverage.info Lines: 34.4 % 285 98
Test Date: 2026-03-21 17:55:27 Functions: 40.0 % 25 10

            Line data    Source code
       1              : // Application include
       2              : #include "appLogger.hpp"
       3              : #include "sysManager.hpp"
       4              : #include "xtoa.hpp"
       5              : #include <algorithm> // For std::find_if
       6              : #include <iterator>  // For std::begin, std::end
       7              : 
       8              : namespace FreeRTOS_Cpp
       9              : {
      10              :     appLogger* appLogger::instance = nullptr;
      11              :     bool appLogger::storageInitStatus = false;
      12              :     void* appLogger::_eraseCompleteMutex = nullptr;
      13              : 
      14            5 :    appLogger::appLogger(IRTOS* rtos, IHardware* hw, appSensorRead* sensorTask, void* sysEvents, void* wdgEvents, void* wdgHardwareHandle) 
      15            5 :         : _rtos(rtos), _hw(hw), _sensorTask(sensorTask), _sysEvents(sysEvents), _wdgEvents(wdgEvents), _watchdogHardware(wdgHardwareHandle)
      16              :     {
      17              :         // Member initialization list is faster and safer than assignment in body
      18            5 :     }
      19              : 
      20            5 :     void appLogger::init(void* printQueue, void* eventQueue, void* commandQueue, 
      21              :                          void* uartMutex, void* qspiMutex, void* eraseCompleteMutex)
      22              :     {
      23              :         // Simply store the injected handles
      24            5 :         _printQueue = printQueue;
      25            5 :         _eventQueue = eventQueue;
      26            5 :         _commandQueue = commandQueue;
      27            5 :         _uartMutex = uartMutex;
      28            5 :         _qspiMutex = qspiMutex;
      29            5 :         _eraseCompleteMutex = eraseCompleteMutex;
      30            5 :         appLogger::instance = this;
      31            5 :     }
      32              : 
      33            8 :     void appLogger::logMessage ( const char *pcMessage, 
      34              :                                 sAppLoggerEventCode_t enumEventCodes)
      35              :     {
      36              :         
      37              :         sAppLoggerMessage_t sLogMsg;
      38            8 :         sLogMsg.enumEventCode = enumEventCodes;
      39              :         
      40            8 :         (void)strncpy(sLogMsg.pcMessage, pcMessage, sizeof(sLogMsg.pcMessage) - 1U);
      41            8 :         sLogMsg.pcMessage[sizeof(sLogMsg.pcMessage) - 1U] = '\0';
      42              : 
      43              :         // Send to queue
      44              :         
      45            8 :         if ((instance != nullptr) && (instance->_printQueue != nullptr)) {
      46            0 :             instance->_rtos->queueSend(instance->_printQueue, &sLogMsg, 0);
      47              :         }
      48            8 :     }
      49              : 
      50            2 :     void appLogger::logEvent(const sStorageEvent_t* event)
      51              :     {
      52              :         // Send to queue
      53            2 :         if ((instance != nullptr) && (instance->_eventQueue != nullptr)) {
      54            0 :             instance->_rtos->queueSend(instance->_eventQueue, event, 0);
      55              :         }
      56            2 :     }
      57              : 
      58            0 :     void appLogger::notifyCommandReceivedFromISR(uint8_t rxChar)
      59              :     {
      60            0 :         bool higherPriorityTaskWoken = false;
      61              :         
      62              :         // Safety check before using abstract RTOS call
      63            0 :         if ((_rtos != nullptr) && (_commandQueue != nullptr)) {
      64            0 :             _rtos->queueSendFromISR(_commandQueue, &rxChar, &higherPriorityTaskWoken);
      65              :         }
      66              :         
      67              :         // Note: FreeRTOS requires a context switch if a higher priority task was woken.
      68              :         // Since we are decoupling, the actual portYIELD_FROM_ISR is handled in 
      69              :         // HAL_QSPI_StatusMatchCallback or the UART ISR directly in main.cpp.
      70            0 :     }
      71              : 
      72              :     //TODO: MOdify it to prepend the EVENT CODE related string before the actual message
      73            0 :     void appLogger::vAppLoggerTask(void *pvParameters)
      74              :     {
      75              :         // Casting back to the instance
      76            0 :         appLogger* self = static_cast<appLogger*>(pvParameters);
      77              :         sAppLoggerMessage_t sLogMsg;
      78              :         sStorageEvent_t sEvent_PageBuffer[16];
      79            0 :         uint8_t eventCount = 0;
      80              : 
      81              :         for (;;)
      82              :         {
      83              :             // Drain print queue
      84            0 :             while (self->_rtos->queueReceive(self->_printQueue, &sLogMsg, 0)) 
      85              :             {
      86            0 :                 if (sLogMsg.enumEventCode == sAPPLOGGER_EVENT_CODE_PRINT_MESSAGE) 
      87              :                 {
      88            0 :                     if (self->_rtos->takeMutex(self->_uartMutex, 0xFFFFFFFF)) 
      89              :                     {
      90            0 :                         self->_hw->printLog(reinterpret_cast<uint8_t*>(sLogMsg.pcMessage), 
      91            0 :                                         static_cast<uint16_t>(strlen(sLogMsg.pcMessage)));
      92            0 :                         self->_rtos->giveMutex(self->_uartMutex);
      93              :                     }
      94              :                 }
      95              :             }
      96              : 
      97              :             // Handle binary events
      98            0 :             bool qStatus = self->_rtos->queueReceive(self->_eventQueue, &sEvent_PageBuffer[eventCount], 100);
      99            0 :             if (qStatus)
     100              :             {
     101            0 :                 eventCount++;
     102              :             }
     103              : 
     104              :             //Flush if buffer is full (16), OR if we have data and the queue timed out waiting for more */
     105            0 :             if ((eventCount >= 16) || (!qStatus && (eventCount > 0)))    
     106              :             {
     107            0 :                 self->flushBufferToFlash(sEvent_PageBuffer, eventCount);
     108            0 :                 eventCount = 0;
     109              :             }
     110              :             
     111            0 :             self->_rtos->setEventBits(self->_wdgEvents, WATCHDOG_BIT_LOGGER);
     112            0 :         }
     113              :     }
     114              : 
     115            0 :     void appLogger::vCommandTask(void *pvParameters)
     116              :     {
     117            0 :         uint8_t rxChar = 0;
     118            0 :         char commandLine[32] = {0};
     119            0 :         uint8_t cmdIndex = 0;
     120            0 :         appLogger *self = static_cast<appLogger*>(pvParameters);
     121              : 
     122              :         /* Start the first interrupt-driven receive */
     123            0 :         self->_hw->startCommandReceiveIT((&self->_rxChar));
     124              : 
     125              :         for (;;)
     126              :         {
     127              :             /* This task will SLEEP for 1000 sec */
     128            0 :             if (self->_rtos->queueReceive(self->_commandQueue, &rxChar, 1000))
     129              :             {
     130            0 :                 if(rxChar == '\n' || rxChar == '\r')
     131              :                 {
     132            0 :                     commandLine[cmdIndex] = '\0';
     133            0 :                     if(cmdIndex > 0)
     134              :                     {
     135            0 :                         self->handleCommand(commandLine);
     136              :                     }
     137            0 :                     cmdIndex = 0;
     138            0 :                     self->sendCommandResponse(">"); // Display '>' on the terminal
     139              :                 }
     140            0 :                 else if (cmdIndex < sizeof(commandLine) - 1)
     141              :                 {
     142            0 :                     commandLine[cmdIndex++] = rxChar;
     143              :                     #if DEBUGGING
     144              :                         self->_hw->printLog(&rxChar, 1); // Display the received character on the terminal
     145              :                     #endif
     146              :                 }
     147              :             }
     148            0 :             self->_rtos->setEventBits(self->_wdgEvents, WATCHDOG_BIT_COMMAND);
     149              :         }
     150              :     }
     151              : 
     152              : /**
     153              :  * @brief Handles commands from the command task
     154              :  *
     155              :  * @param cmd Command string received from the command task
     156              :  *
     157              :  * @details Handles commands from the command task. Supports the following commands:
     158              :  *  - ping: Responds with "pong\r\n"
     159              :  *  - help: Prints the command help menu
     160              :  *  - dump_logs: Dumps the event logs
     161              :  *  - event_sector_erase: Erases the event log sector
     162              :  *  - bulk_erase: Performs a bulk erase of the storage
     163              :  *  - stack_health: Checks the stack health
     164              :  *  - get_temp: Gets the current temperature
     165              :  *  - get_humidity: Gets the current humidity
     166              :  *
     167              :  */
     168            3 :     void appLogger::handleCommand(const char* cmd)
     169              :     {
     170              :         // 1. Define a structure for our command table
     171              :         struct CommandEntry {
     172              :             const char* name;
     173              :             void (appLogger::*handler)();
     174              :         };
     175              : 
     176              : 
     177              :         // 2. The Command Table (Internal to function or class member)
     178              :         static const CommandEntry commandTable[] = {
     179              :             {"ping",                 &appLogger::cmdPing},
     180              :             {"help",                 &appLogger::cmdHelp},
     181              :             {"dump_logs",            &appLogger::dumpLogs},
     182              :             {"event_sector_erase",   &appLogger::eventSectorErase},
     183              :             {"bulk_erase",           &appLogger::storageBulkErase},
     184              :             {"stack_health",         &appLogger::checkStackUsage},
     185              :             {"get_temp",             &appLogger::cmdGetTemp},
     186              :             {"get_humidity",         &appLogger::cmdGetHumidity},
     187              :             {"enable_temp_log",      &appLogger::cmdEnableTempLog},
     188              :             {"disable_temp_log",     &appLogger::cmdDisableTempLog},
     189              :             {"enable_humidity_log",  &appLogger::cmdEnableHumLog},
     190              :             {"disable_humidity_log", &appLogger::cmdDisableHumLog}
     191              :         };
     192              : 
     193              :         // 3. STL Dispatcher logic using std::find_if
     194            9 :         auto it = std::find_if(std::begin(commandTable), 
     195              :                                std::end(commandTable),
     196           21 :                                [cmd](const CommandEntry& entry) 
     197              :             {
     198           21 :                 return strncmp(cmd, entry.name, strlen(entry.name)) == 0;
     199              :             });
     200              : 
     201              :         // 4. Check if the command was found
     202            3 :         if (it != std::end(commandTable)) 
     203              :         {
     204            2 :             (this->*(it->handler))(); // Call the member function
     205              :         } 
     206              :         else 
     207              :         {
     208            1 :             sendCommandResponse("\r\nCommand: Invalid command\r\n");
     209              :         }
     210            3 :     }
     211              : 
     212            0 :     void appLogger::cmdPing() 
     213              :     {
     214            0 :         sendCommandResponse("\r\npong\r\n");
     215            0 :     }
     216              : 
     217            0 :     void appLogger::cmdGetTemp() {
     218              :         char tempBuf[16]; // Increased for safety
     219            0 :         sendCommandResponse("\r\nCurrent Temperature: ");
     220              :         
     221              :         // FIX: Use sizeof(tempBuf) instead of hardcoded '6' to prevent safety return
     222            0 :         xtoa::app_ftoa(_sensorTask->getCurrentTemp(), tempBuf, (uint32_t)sizeof(tempBuf));
     223              :         
     224            0 :         sendCommandResponse(tempBuf);
     225            0 :         sendCommandResponse(" C\r\n"); // Segmented printing saves ~40 bytes of stack
     226            0 :     }
     227              : 
     228            0 :     void appLogger::cmdGetHumidity() {
     229              :         char humBuf[16];
     230            0 :         sendCommandResponse("\r\nCurrent Humidity: ");
     231            0 :         xtoa::app_ftoa(_sensorTask->getCurrentHumidity(), humBuf, (uint32_t)sizeof(humBuf));
     232            0 :         sendCommandResponse(humBuf);
     233            0 :         sendCommandResponse(" %\r\n");
     234            0 :     }
     235              : 
     236            0 :     void appLogger::cmdHelp() {
     237            0 :         sendCommandResponse("\r\n--- Command Help Menu ---\r\n"
     238              :                             "dump_logs:             Dump Logs\r\n"
     239              :                             "bulk_erase:            Bulk Erase\r\n"
     240              :                             "stack_health:          Stack Health\r\n"
     241              :                             "get_temp:              Get Temperature\r\n"
     242              :                             "get_humidity:          Get Humidity\r\n"
     243              :                             "enable_temp_log:       Enable Temperature Logging\r\n"
     244              :                             "disable_temp_log:      Disable Temperature Logging\r\n"
     245              :                             "enable_humidity_log:   Enable Humidity Logging\r\n"
     246              :                             "disable_humidity_log:  Disable Humidity Logging\r\n"
     247              :                             "event_sector_erase:    Event log sector erase\r\n");
     248            0 :     }
     249              : 
     250            0 :     void appLogger::cmdEnableTempLog() {
     251            0 :         _sensorTask->enableTemperatureLogging();
     252            0 :         sendCommandResponse("\r\nCommand: Enabled Temperature Logging\r\n");
     253            0 :     }
     254              : 
     255            0 :     void appLogger::cmdDisableTempLog() {
     256            0 :         _sensorTask->disableTemperatureLogging();
     257            0 :         sendCommandResponse("\r\nCommand: Disabled Temperature Logging\r\n");
     258            0 :     }
     259              : 
     260            0 :     void appLogger::cmdEnableHumLog() {
     261            0 :         _sensorTask->enableHumidityLogging();
     262            0 :         sendCommandResponse("\r\nCommand: Enabled Humidity Logging\r\n");
     263            0 :     }
     264              : 
     265            0 :     void appLogger::cmdDisableHumLog() {
     266            0 :         _sensorTask->disableHumidityLogging();
     267            0 :         sendCommandResponse("\r\nCommand: Disabled Humidity Logging\r\n");
     268            0 :     }
     269              : 
     270              :     /* Internal function to dump Flash log (if found) on UART */
     271            1 :     void appLogger::dumpLogs(void)
     272              :     {
     273              :         sStorageEvent_t tempEvent;
     274              :         sLogSectorHeader_t sectorHeader;
     275              :         
     276              :         static char outMsg[128] = {0};
     277              :         static char valBuf[16] = {0};
     278              : 
     279            1 :         if (_rtos->takeMutex(_qspiMutex, 0xFFFFFFFF))
     280              :         {
     281            1 :             if(_rtos->takeMutex(_uartMutex, 0xFFFFFFFF))
     282              :             {
     283              :                 /* 2. READ AND PRINT SECTOR HEADER */
     284            1 :                 if (_hw->storageRead(reinterpret_cast<uint8_t*>(&sectorHeader), LOG_PARTITION_START, LOG_HEADER_SIZE))
     285              :                 {
     286            1 :                     (void)strcpy(outMsg, "--- FLASH LOG DUMP START ---\r\n--- SECTOR HEADER ---\r\nSignature: ");
     287            1 :                     xtoa::app_itoa(sectorHeader.magicSignature, valBuf, 15);
     288            1 :                     (void)strcat(outMsg, valBuf);
     289            1 :                     (void)strcat(outMsg, "\r\nVersion: ");
     290            1 :                     xtoa::app_itoa(sectorHeader.version, valBuf, 10);
     291            1 :                     (void)strcat(outMsg, valBuf);
     292            1 :                     (void)strcat(outMsg, "\r\nmax event: ");
     293            1 :                     xtoa::app_itoa(sectorHeader.maxEvents, valBuf, 10);
     294            1 :                     (void)strcat(outMsg, valBuf);
     295            1 :                     (void)strcat(outMsg, "\r\nErase Count: ");
     296            1 :                     xtoa::app_itoa(sectorHeader.eraseCount, valBuf, 12);
     297            1 :                     (void)strcat(outMsg, valBuf);
     298            1 :                     (void)strcat(outMsg, "\r\n---------------------\r\n");
     299            1 :                     _hw->printLog(reinterpret_cast<const uint8_t*>(outMsg), static_cast<uint16_t>(strlen(outMsg)));
     300              :                 }
     301              : 
     302            1 :                 uint32_t readAddr = LOG_DATA_START; // Start after sector header data
     303            2 :                 while (readAddr < _u32CurrentWriteAddress)
     304              :                 {
     305            1 :                     if (_hw->storageRead(reinterpret_cast<uint8_t*>(&tempEvent), readAddr, LOG_ENTRY_SIZE))
     306              :                     {
     307              :                         /* Only process known sensor data points */
     308            1 :                         if (tempEvent.eventID == EVENT_ID_T_SENSOR_DATA_POINT || tempEvent.eventID == EVENT_ID_H_SENSOR_DATA_POINT)
     309              :                         {                      
     310            0 :                             float sensorValue = 0.0f; // Use a generic name for clarity
     311            0 :                             (void)memcpy(&sensorValue, &tempEvent.payload[0], sizeof(float));
     312            0 :                             (void)memset(outMsg, 0, 128);
     313            0 :                             (void)strcpy(outMsg, "Event log: ");
     314              : 
     315            0 :                             xtoa::app_itoa(tempEvent.timestamp, valBuf, 10);
     316            0 :                             (void)strcat(outMsg, valBuf);
     317              : 
     318            0 :                             (void)strcat(outMsg, (tempEvent.eventID == EVENT_ID_T_SENSOR_DATA_POINT) ? " | T: " : " | H: ");
     319            0 :                             xtoa::app_ftoa(sensorValue, valBuf, 6);
     320            0 :                             (void)strcat(outMsg, valBuf);
     321            0 :                             (void)strcat(outMsg, "\r\n");
     322            0 :                             _hw->printLog(reinterpret_cast<const uint8_t*>(outMsg), static_cast<uint16_t>(strlen(outMsg)));
     323              :                         }
     324              :                     }
     325            1 :                     readAddr += LOG_ENTRY_SIZE;
     326              :                     
     327              :                     /* Add a tiny delay so we don't overwhelm the UART or starve the Heartbeat */
     328            1 :                     _rtos->delay(1);
     329              :                 }
     330            1 :                 (void)strcpy(outMsg, "--- FLASH LOG DUMP END ---\r\n");
     331            1 :                 _hw->printLog(reinterpret_cast<const uint8_t*>(outMsg), static_cast<uint16_t>(strlen(outMsg)));
     332            1 :                 _rtos->delay(1);
     333            1 :                 _rtos->giveMutex(_uartMutex);
     334              :             }
     335            1 :             _rtos->giveMutex(_qspiMutex);
     336              :         }
     337            1 :     }
     338              : 
     339              :     /* Internal function to find where we left off */
     340            0 :     void appLogger::scanWriteHead(void)
     341              :     {
     342              :         sStorageEvent_t tempEvent;
     343              :         /* Lock the hardware for the initial scan */
     344            0 :         if (_rtos->takeMutex(_qspiMutex, 0xFFFFFFFFu))
     345              :         {
     346            0 :             bool headFound = false;
     347            0 :             uint32_t scanAddr = LOG_DATA_START; //first 16 bytes reserved for sector header data.
     348              :             /* Loop through flash in increments of our struct size */
     349            0 :             while (scanAddr < LOG_PARTITION_END)
     350              :             {
     351              :                 //Read only 4 bytes of event ID size
     352            0 :                 if (_hw->storageRead(reinterpret_cast<uint8_t *>(&tempEvent), scanAddr, 4))
     353              :                 {
     354              :                     /* 0xFFFFFFFF means this 'slot' is empty */
     355            0 :                     if (tempEvent.timestamp == 0xFFFFFFFFU)
     356              :                     {
     357            0 :                         _u32CurrentWriteAddress = scanAddr;
     358            0 :                         headFound = true;
     359            0 :                         break;
     360              :                     }
     361              :                 }
     362            0 :                 scanAddr += LOG_ENTRY_SIZE;
     363              :             }
     364              : 
     365            0 :             if (!headFound)
     366              :             {
     367              :                 sLogSectorHeader_t currentHeader;
     368              :                 /* Flash log sector is completely full, start at LOG_DATA_START */
     369              :                 /* Read the old header to get the current erase count */
     370              :                 //(void)memset(&currentHeader, 0, LOG_HEADER_SIZE);
     371            0 :                 if (_hw->storageRead(reinterpret_cast<uint8_t*>(&currentHeader), LOG_PARTITION_START, LOG_HEADER_SIZE))
     372              :                 {
     373            0 :                     currentHeader.eraseCount++;
     374              :                 }
     375              :                 else
     376              :                 {
     377            0 :                     currentHeader.eraseCount = 1; // Fallback
     378              :                 }
     379              : 
     380              :                 /* Wipe the sector to make room for new logs */
     381            0 :                 _hw->storageEraseSector(LOG_PARTITION_START);
     382            0 :                 while (_hw->storageIsBusy()) { _rtos->delay(1); }
     383              : 
     384              :                 /* 3. Re-write the header with the new count */
     385            0 :                 currentHeader.magicSignature = STORAGE_EVENT_SECTOR_MAGIC_SIGNATURE;
     386            0 :                 currentHeader.version = LOG_EVENT_SECTOR_VERSION;
     387            0 :                 currentHeader.maxEvents = MAX_LOG_EVENTS;
     388            0 :                 _hw->storageWrite(reinterpret_cast<uint8_t*>(&currentHeader), LOG_PARTITION_START, LOG_HEADER_SIZE);
     389              : 
     390            0 :                 _u32CurrentWriteAddress = LOG_DATA_START;
     391            0 :                 logMessage("Storage: Log Partition Full. Sector Erased & Wrapped.\r\n", 
     392              :                                         sAPPLOGGER_EVENT_CODE_PRINT_MESSAGE);
     393              :             }
     394              :             else
     395              :             {
     396              :                 char addrBuf[16];
     397            0 :                 char msg[64] = "Storage: Resuming at addr: ";
     398            0 :                 xtoa::app_itoa((int32_t)_u32CurrentWriteAddress, addrBuf, 10);
     399            0 :                 (void)strncat(msg, addrBuf, sizeof(msg) - strlen(msg) - 1U);
     400            0 :                 (void)strncat(msg, "\r\n", sizeof(msg) - strlen(msg) - 1U);
     401            0 :                 logMessage(msg, sAPPLOGGER_EVENT_CODE_PRINT_MESSAGE);
     402              :             }
     403            0 :             _rtos->giveMutex(_qspiMutex);
     404              :         }
     405            0 :     }
     406              : 
     407            0 :     void appLogger::eventSectorErase(void)
     408              :     {
     409            0 :         logMessage("Storage: Starting Event log sector Erase (Wait ~5s)...\r\n", 
     410              :                             sAPPLOGGER_EVENT_CODE_PRINT_MESSAGE);
     411              : 
     412            0 :         if (_rtos->takeMutex(_qspiMutex, 0xFFFFFFFF))
     413              :         {
     414              :             sLogSectorHeader_t currentHeader;
     415              :             /* Flash log sector is completely full, start at LOG_DATA_START */
     416              :             /* Read the old header to get the current erase count */
     417              :             //(void)memset(&currentHeader, 0, LOG_HEADER_SIZE);
     418            0 :             if (_hw->storageRead(reinterpret_cast<uint8_t*>(&currentHeader), LOG_PARTITION_START, LOG_HEADER_SIZE))
     419              :             {
     420            0 :                 currentHeader.eraseCount++;
     421              :             }
     422              :             else
     423              :             {
     424            0 :                 currentHeader.eraseCount = 1; // Fallback
     425              :             }
     426              : 
     427              :             /* Wipe the sector to make room for new logs */
     428            0 :             _hw->storageEraseSector(LOG_PARTITION_START);
     429            0 :             while (_hw->storageIsBusy()) 
     430              :             { 
     431            0 :                 _rtos->delay(50);
     432            0 :                 _hw->watchdog_refresh(_watchdogHardware);
     433            0 :                 _rtos->setEventBits(_wdgEvents, WATCHDOG_BIT_COMMAND);
     434              :             }
     435              : 
     436              :             /* 3. Re-write the header with the new count */
     437            0 :             currentHeader.magicSignature = STORAGE_EVENT_SECTOR_MAGIC_SIGNATURE;
     438            0 :             currentHeader.version = LOG_EVENT_SECTOR_VERSION;
     439            0 :             currentHeader.maxEvents = MAX_LOG_EVENTS;
     440            0 :             _hw->storageWrite(reinterpret_cast<uint8_t*>(&currentHeader), LOG_PARTITION_START, LOG_HEADER_SIZE);
     441              : 
     442            0 :             _u32CurrentWriteAddress = LOG_DATA_START;
     443            0 :             _rtos->giveMutex(_qspiMutex);
     444              : 
     445            0 :             sendCommandResponse("Storage: Event log sector Erase Complete.\r\n");
     446              :         }
     447            0 :     } 
     448              : 
     449            0 :     void appLogger::storageBulkErase(void)
     450              :     {
     451            0 :         logMessage("Storage: Starting Full Chip Erase (Wait ~25s)...\r\n", sAPPLOGGER_EVENT_CODE_PRINT_MESSAGE);
     452              : 
     453            0 :         if (_rtos->takeMutex(_qspiMutex, 0xFFFFFFFF))
     454              :         {
     455            0 :             if (_hw->storageBulkErase()) {
     456            0 :                 while (_hw->storageIsBusy()) {
     457            0 :                     _rtos->delay(100);
     458            0 :                     _hw->watchdog_refresh(_watchdogHardware);
     459            0 :                     _rtos->setEventBits(_wdgEvents, WATCHDOG_BIT_COMMAND); 
     460              :                 }
     461              :             }
     462            0 :             sendCommandResponse("Storage: Bulk Erase Complete.\r\n");
     463            0 :             sLogSectorHeader_t currentHeader = {0};
     464            0 :             currentHeader.magicSignature = STORAGE_EVENT_SECTOR_MAGIC_SIGNATURE;
     465            0 :             currentHeader.version = LOG_EVENT_SECTOR_VERSION;
     466            0 :             currentHeader.maxEvents = MAX_LOG_EVENTS;
     467            0 :             currentHeader.eraseCount = 1;
     468              :             
     469            0 :             if (!_hw->storageWrite(reinterpret_cast<uint8_t*>(&currentHeader), LOG_PARTITION_START, LOG_HEADER_SIZE)) {
     470            0 :                 logMessage("CRITICAL: Failed to write Sector Header!\r\n", sAPPLOGGER_EVENT_CODE_LOG_ERROR);
     471              :             }
     472              :             
     473            0 :             _u32CurrentWriteAddress = LOG_DATA_START;
     474            0 :             _rtos->giveMutex(_qspiMutex);
     475              :         }
     476            0 :     }
     477              : 
     478            2 :     uint8_t appLogger::storageInit(void)
     479              :     {
     480              :         uint32_t flashSize, eraseSize, progSize;
     481            2 :         char qspiMsg[LOGGER_MESSAGE_STR_LEN] = "QSPI Init OK | Size: ";
     482              :         char sizeBuf[16];
     483            2 :         uint8_t ret = 0; // pdFAIL equivalent
     484            2 :         sStorageEvent_t sEvent = {0};
     485              :         sLogSectorHeader_t header;
     486              : 
     487            2 :         if(_hw->storageInit(&flashSize, &eraseSize, &progSize))
     488              :         {
     489            2 :             uint32_t flashSizeMB = flashSize / (1024U * 1024U);
     490              :             
     491            2 :             if(_hw->storageRead(reinterpret_cast<uint8_t*>(&header), LOG_PARTITION_START, LOG_HEADER_SIZE))
     492              :             {
     493            0 :                 if(header.magicSignature != STORAGE_EVENT_SECTOR_MAGIC_SIGNATURE) {
     494            0 :                     logMessage("Storage: First-time boot detected. Erasing chip...\r\n", sAPPLOGGER_EVENT_CODE_PRINT_MESSAGE);
     495            0 :                     storageBulkErase(); 
     496            0 :                     _u32CurrentWriteAddress = LOG_HEADER_SIZE; 
     497              :                 } else {
     498            0 :                     logMessage("Storage: Magic Signature Found. Scanning flash...\r\n", sAPPLOGGER_EVENT_CODE_PRINT_MESSAGE);
     499            0 :                     scanWriteHead();
     500              :                 }
     501              :             }
     502              : 
     503            2 :             sEvent.timestamp = _rtos->getTickCount();
     504            2 :             sEvent.eventID   = EVENT_ID_QSPI_INIT_SUCCESS;
     505            2 :             sEvent.taskID    = TASK_ID_SYS_MANAGER; 
     506            2 :             sEvent.payload[0] = eraseSize;      
     507            2 :             sEvent.payload[1] = progSize;         
     508              : 
     509            2 :             logEvent(&sEvent);
     510            2 :             xtoa::app_itoa((int32_t)flashSizeMB, sizeBuf, 10);
     511            2 :             (void)strncat(qspiMsg, sizeBuf, sizeof(qspiMsg) - strlen(qspiMsg) - 1U);
     512            2 :             (void)strncat(qspiMsg, " MB\r\n", sizeof(qspiMsg) - strlen(qspiMsg) - 1U);
     513              : 
     514            2 :             logMessage(qspiMsg, sAPPLOGGER_EVENT_CODE_PRINT_MESSAGE);
     515            2 :             ret = 1; // pdPASS equivalent
     516              :         }
     517            2 :         return ret;
     518              :     }
     519              : 
     520            0 :     void appLogger::flushBufferToFlash(sStorageEvent_t *pBuffer, uint8_t eventCount)
     521              :     {
     522            0 :         uint32_t writeSize = (uint32_t)eventCount * LOG_ENTRY_SIZE;
     523              :         sLogSectorHeader_t currentHeader;
     524              :         
     525            0 :         if (_rtos->takeMutex(_qspiMutex, 0xFFFFFFFF))
     526              :         {
     527            0 :             if ((_u32CurrentWriteAddress + writeSize) >  LOG_PARTITION_END)
     528              :             {
     529            0 :                 logMessage("Storage: Erasing Event logging Sector...\r\n", sAPPLOGGER_EVENT_CODE_PRINT_MESSAGE);
     530            0 :                 if (_hw->storageRead(reinterpret_cast<uint8_t*>(&currentHeader), LOG_PARTITION_START, LOG_HEADER_SIZE)) {
     531            0 :                     currentHeader.eraseCount++; 
     532              :                 } else {
     533            0 :                     currentHeader.eraseCount = 1; 
     534              :                 }
     535              : 
     536            0 :                 _hw->storageEraseSector(LOG_PARTITION_START);
     537              :                 
     538            0 :                 while (_hw->storageIsBusy()) 
     539              :                 {
     540            0 :                     _rtos->delay(50); 
     541            0 :                     _hw->watchdog_refresh(_watchdogHardware);
     542            0 :                     _rtos->setEventBits(_wdgEvents, WATCHDOG_BIT_LOGGER);
     543              :                 }
     544              : 
     545            0 :                 currentHeader.magicSignature = STORAGE_EVENT_SECTOR_MAGIC_SIGNATURE;
     546            0 :                 currentHeader.version = LOG_EVENT_SECTOR_VERSION;
     547            0 :                 currentHeader.maxEvents = MAX_LOG_EVENTS;
     548            0 :                 _hw->storageWrite(reinterpret_cast<uint8_t*>(&currentHeader), LOG_PARTITION_START, LOG_HEADER_SIZE);
     549              : 
     550            0 :                 _u32CurrentWriteAddress = LOG_DATA_START;
     551            0 :                 logMessage("Storage: Sector Wrapped & Erase Count Updated\r\n", sAPPLOGGER_EVENT_CODE_PRINT_MESSAGE);
     552              :             }
     553              : 
     554            0 :             if (_hw->storageWrite(reinterpret_cast<uint8_t*>(pBuffer), _u32CurrentWriteAddress, writeSize))
     555              :             {
     556              :                 static sStorageEvent_t readBackBuffer[16]; 
     557            0 :                 if (_hw->storageRead(reinterpret_cast<uint8_t*>(readBackBuffer), _u32CurrentWriteAddress, writeSize))
     558              :                 {
     559            0 :                     if (memcmp(reinterpret_cast<const void*>(pBuffer), reinterpret_cast<const void*>(readBackBuffer), writeSize) != 0) {
     560            0 :                         logMessage("CRITICAL: Storage Verification Failed! Bit-flip detected.\r\n", sAPPLOGGER_EVENT_CODE_PRINT_MESSAGE);
     561              :                     }
     562              :                 }
     563              :             }
     564              :         }
     565            0 :         _u32CurrentWriteAddress += writeSize;
     566            0 :         _rtos->giveMutex(_qspiMutex);
     567            0 :     } 
     568              : 
     569            1 :     void appLogger::checkStackUsage(void)
     570              :     {
     571              :         char valBuf[16];
     572            1 :         const char* taskName = nullptr;
     573            1 :         void* taskHandle = nullptr;
     574            1 :         sendCommandResponse("\r\n--- Task Stack High Water Marks (Words Remaining) ---\r\n");
     575              : 
     576            1 :         uint32_t count = _rtos->getRegisteredTaskCount();
     577            2 :         for (uint32_t i = 0; i < count; i++)
     578              :         {
     579            1 :             if (_rtos->getRegisteredTaskInfo(i, &taskName, &taskHandle)) 
     580              :             {
     581              :                 // 1. Print Task Name
     582            1 :                 sendCommandResponse(taskName);
     583            1 :                 sendCommandResponse(": ");
     584              : 
     585              :                 // 2. Get and convert Water Mark
     586              :                 // Note: uxTaskGetStackHighWaterMark returns the minimum free stack space 
     587              :                 // seen since the task started. Lower = Closer to overflow.
     588            1 :                 uint32_t stackRemaining = _rtos->getStackHighWaterMark(taskHandle);
     589            1 :                 xtoa::app_itoa(static_cast<int32_t>(stackRemaining), valBuf, 10);
     590              :                 
     591              :                 // 3. Print Value
     592            1 :                 sendCommandResponse(valBuf);
     593            1 :                 sendCommandResponse(" words\r\n");
     594              :             } 
     595              :             else 
     596              :             {
     597            0 :                 sendCommandResponse(taskName);
     598            0 :                 sendCommandResponse(": NOT_STARTED\r\n");
     599              :             }
     600              :         }
     601            1 :         sendCommandResponse("----------------------------------------------------\r\n");
     602            1 :     }
     603              : 
     604            7 :     void appLogger::sendCommandResponse(const char *pMsg)
     605              :     {
     606            7 :         if (_rtos->takeMutex(_uartMutex, 100))
     607              :         {
     608            7 :             _hw->printLog(reinterpret_cast<const uint8_t*>(pMsg), (uint16_t)strlen(pMsg));
     609            7 :             _rtos->giveMutex(_uartMutex);
     610              :         }
     611            7 :     }
     612              : }
        

Generated by: LCOV version 2.0-1