diff --git a/src/trace.cpp b/src/trace.cpp new file mode 100644 index 0000000..c425de9 --- /dev/null +++ b/src/trace.cpp @@ -0,0 +1,278 @@ +#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 * 4) + 1]; + int lineptr = 0; + + while (0 != *buffer) + { + switch (*buffer)// uprava escape sekvenci pro JSON/HTML + { + case '"': + lin[lineptr++] = '\\'; + lin[lineptr] = '"'; + break; + + case '\\': + lin[lineptr++] = '\\'; + lin[lineptr] = '\\'; + break; + + case '/': + lin[lineptr++] = '\\'; + lin[lineptr] = '/'; + break; + + case '<': + strcpy_P(&lin[lineptr], PSTR("<")); + lineptr += 3; + break; + + case '>': + strcpy_P(&lin[lineptr], PSTR(">")); + lineptr += 3; + break; + + default: + if (*buffer > 0x1f) + lin[lineptr] = *buffer; + else + lin[lineptr] = '?'; + break; + } + ++lineptr; + ++buffer; + } + if (lineptr > MAX_LINE_LEN) + lineptr = MAX_LINE_LEN; + 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...")); +} + +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(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 diff --git a/src/trace.h b/src/trace.h new file mode 100644 index 0000000..0641f9c --- /dev/null +++ b/src/trace.h @@ -0,0 +1,123 @@ +/** + * @file trace.h + * @author Pavel Brychta, http://www.xpablo.cz + * + * Copyright (c) 2016-18 Pavel Brychta. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ +#ifndef _TRACE_H_ +#define _TRACE_H_ + +#include +#include + +// Definice jednotlivych typu hlaseni do trasovani +#define TRACE_ERROR 0 // chybova zprava = cervena +#define TRACE_WARNING 1 // varovani - zluta +#define TRACE_INFO 2 // informacni zprava - zelena +#define TRACE_DEBUG 3 // ladici zprava - cerna +#define TRACE_DEBUGMORE 4 // mene zajimava ladici zprava - take cerna + +#define TRACE_CHECK_INTERVAL 200 // interval [ms], po kterem je testovano odesilani stopare + +/** + * @brief Inicializace modulu + */ +void trace_init(void); + +/** + * @brief Ziskani vypisu v JSON formatu + * + * @param str retezec udalosti v JSON + */ +void trace_dumpJSON(String &str); + +/** + * @brief Vyprazdneni stopovaciho bufferu + */ +void trace_clear(void); + +/** + * @brief Ukonceni prace stopare - vyprazdni buffer + */ +void trace_end(void); + +/** + * @brief Ulozeni zpravy s obsahem z programove pameti (PROGMEM, F, ...) + * + * @param[in] severity Uroven - viz. TRACE_ERROR, TRACE_WARNING, ... + * @param[in] fmt Formatovaci retezec vysledne zpravy. Je ulozeny v PROGMEM + * @param[in] Parametry, ktere jsou dosazeny do formatovaciho retezce + */ +void trace_print(uint8_t severity, const __FlashStringHelper *fmt, ...); + +/** + * @brief Ulozeni zpravy + * + * @param[in] severity Uroven - viz. TRACE_ERROR, TRACE_WARNING, ... + * @param[in] fmt Formatovaci retezec vysledne zpravy. + * @param[in] Parametry, ktere jsou dosazeny do formatovaciho retezce + */ +void trace_print(uint8_t severity, const char *fmt, ...); + +/** + * @brief Registrace callback metody - je volana pri kazde zmene (zapisu) do stopare + * + * @param[in] Ukazatel na volanou metodu + */ +void trace_registermessagecb(void (*)(const char *msg)); + +/** + * @brief Pridani obsluhy stranky /trace do weboveho serveru + * + * @param socket webovy soket pro komunikaci + */ +void trace_addweb(AsyncWebSocket *socket); + +/** + * @brief Periodicky tik modulem - osetruje odeslani novych dat v pripade modifikace + */ +void trace_poll(); + +void trace_printfunc(uint8_t severity, const char *func, const char *file, int line, const char *fmt, ...); + +/** + * @brief Vypis oblasti pameti (dump) + * + * @param[in] severity Uroven - viz. TRACE_ERROR, TRACE_WARNING, ... + * @param[in] prefix Prefix (text), ktery je uvedeny pred vlastnim vypisem pameti + * @param address Adresa pocatku vypisu + * @param[in] size Pocet vypisovanych bytu (POZOR!!! musi se vejit do jednoho radku stopare!!!) + */ +void trace_dump(uint8_t severity, const char *prefix, uint8_t *address, size_t size); + +/** + * @brief Vypis oblasti pameti (dump) + * + * @param[in] severity Uroven - viz. TRACE_ERROR, TRACE_WARNING, ... + * @param[in] prefix Prefix (text), ktery je uvedeny pred vlastnim vypisem pameti + * @param address Adresa pocatku vypisu + * @param[in] size Pocet vypisovanych bytu (POZOR!!! musi se vejit do jednoho radku stopare!!!) + */ +void trace_dump(uint8_t severity, const __FlashStringHelper *prefix, uint8_t *address, size_t size); + +/** + * @brief Vynuceni odeslani obsahu bufferu (napr. pri obnovovani spojeni) + */ +void trace_forceupdate(void); + +#endif // _TRACE_H_