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*>(§orHeader), 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(¤tHeader, 0, LOG_HEADER_SIZE);
371 0 : if (_hw->storageRead(reinterpret_cast<uint8_t*>(¤tHeader), 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*>(¤tHeader), 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(¤tHeader, 0, LOG_HEADER_SIZE);
418 0 : if (_hw->storageRead(reinterpret_cast<uint8_t*>(¤tHeader), 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*>(¤tHeader), 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*>(¤tHeader), 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*>(¤tHeader), 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*>(¤tHeader), 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 : }
|