#include "trace.h" #include #define MAX_TRACE_LINES 15 #define MAX_LINE_LEN 50 struct TraceLine { char _text[MAX_LINE_LEN + 1]; uint32_t _time; uint8_t _severity; TraceLine(uint8_t severity, const char *str, uint16_t len) { strncpy(_text, str, sizeof(_text)); _text[MAX_LINE_LEN + 1] = 0; _time = millis(); _severity = severity; } TraceLine(void) {} }; static TraceLine _lines[MAX_TRACE_LINES]; static uint16_t _lines_index = 0; static uint16_t _lines_count = 0; static int _modified = 0; static AsyncWebSocket *_wss = NULL; // webovy soket pro trasovani static Interval _tint; // interval pro casovani stopare static void (*message_cb)(const char *) = NULL; static int modulo(int a, int b) { int r = a % b; return ((r < 0) ? r + b : r); } static TraceLine &trace_line(uint16_t index) { int start = _lines_index - _lines_count; int idx = modulo(start + index, _lines_count); return (_lines[idx]); } static void print(uint8_t severity, const char *buffer, int length) { char lin[MAX_LINE_LEN + 1]; int lineptr = 0; while ((0 != *buffer) && (lineptr < (sizeof(lin) - 1))) { if (*buffer > 0x1f) lin[lineptr] = *buffer; else lin[lineptr] = '?'; ++lineptr; ++buffer; } lin[lineptr] = 0; // ukoncime retezec TraceLine line(severity, lin, lineptr); _lines[_lines_index++] = line; _lines_index %= MAX_TRACE_LINES; if (_lines_count < MAX_TRACE_LINES) { ++_lines_count; } ++_modified; } void trace_init(void) { trace_print(TRACE_INFO, F("Trace: Starting...")); } static String _getText(const char *buffer) { String res; res.reserve(MAX_LINE_LEN * 4); while (0 != *buffer) { switch (*buffer)// uprava escape sekvenci pro JSON/HTML { case '"': res.concat(F("\\\"")); break; case '\\': res.concat(F("\\\\")); break; case '/': res.concat(F("\\/")); break; case '<': res.concat(F("<")); break; case '>': res.concat(F(">")); break; default: res.concat(*buffer); break; } ++buffer; } return res; } void trace_dumpJSON(String &str) { for (int i=0; i<_lines_count; i++) { TraceLine line = trace_line(i); if (0 != i) str.concat(F(",")); str.concat(F("{\"t\":")); str.concat(line._time); str.concat(F(",\"s\":")); str.concat(line._severity); str.concat(F(",\"d\":\"")); str.concat(_getText(line._text)); str.concat(F("\"}")); } } void trace_clear(void) { _lines_index = 0; _lines_count = 0; _modified = 1; } void trace_end(void) { trace_clear(); } void trace_print(uint8_t severity, const __FlashStringHelper *fmt, ...) { char buffer[MAX_LINE_LEN + 1]; va_list args; int length; va_start(args, fmt); length = vsnprintf_P(buffer, sizeof (buffer), (const char *)fmt, args); va_end(args); print(severity, buffer, length); } void trace_print(uint8_t severity, const char *fmt, ...) { char buffer[MAX_LINE_LEN + 1]; va_list args; int length; va_start(args, fmt); length = vsnprintf(buffer, sizeof(buffer), fmt, args); va_end(args); print(severity, buffer, length); } void trace_registermessagecb(void (*cb)(const char *)) { message_cb = cb; } void trace_addweb(AsyncWebSocket *socket) { _wss = socket; } static char hexascii(uint8_t n) { n &= 0xf; if (n > 9) return n + ('A' - 10); else return n + '0'; } void trace_dump(uint8_t severity, const char *prefix, uint8_t *address, size_t size) { char buffer[MAX_LINE_LEN + 1 + 3]; int idx = 0; if (prefix) idx = snprintf_P(buffer, MAX_LINE_LEN, PSTR("%s"), prefix); while ((idx < MAX_LINE_LEN) && size) { buffer[idx] = hexascii(*address >> 4); ++idx; buffer[idx] = hexascii(*address); ++idx; buffer[idx] = 0x20; ++idx; ++address; --size; } buffer[idx] = 0; print(severity, buffer, idx); } // TODO: POZOR!!! tady si nejsem jisty, zda je spravne pouziti __FlashStringHelperu v snprintf_P !!!! void trace_dump(uint8_t severity, const __FlashStringHelper *prefix, uint8_t *address, size_t size) { char buffer[MAX_LINE_LEN + 1 + 3]; int idx = 0; if (prefix) idx = snprintf_P(buffer, MAX_LINE_LEN, PSTR("%s"), prefix); while ((idx < MAX_LINE_LEN) && size) { buffer[idx] = hexascii(*address >> 4); ++idx; buffer[idx] = hexascii(*address); ++idx; buffer[idx] = 0x20; ++idx; ++address; --size; } buffer[idx] = 0; print(severity, buffer, idx); } void trace_forceupdate(void) { ++_modified; // vynutime odeslani informaci } void trace_poll() { if (NULL != _wss) { // je definovany webovy soket if (_modified) { // mame nejakou zmenu if (_wss->count() != 0) { // mame klienty if (_tint.expired()) { // .. a vyprsel timeout pro obcerstvovani String log; if (log.reserve((MAX_TRACE_LINES * MAX_LINE_LEN) + (MAX_TRACE_LINES * 50))) { log = F("{\"type\":\"trace\",\"data\":["); trace_dumpJSON(log); log.concat(F("]}")); _wss->textAll(log); } else _wss->textAll(F("{\"type\":\"trace\",\"text\":\"Memory error\"}")); _modified = 0; // rusime pozadavek na odeslani novych dat _tint.set(TRACE_CHECK_INTERVAL); } } else _modified = 0; // zadny pripojeny klient - po pripojeni stejne musime vyzadat stav, takze ted muzeme modifikaci klidne ignorovat } } } // EOF