Utils.h
| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | #ifndef utils_h | ||
| 2 | #define utils_h | ||
| 3 | #include <Arduino.h> | ||
| 4 | #include <atomic> | ||
| 5 | |||
| 6 | |||
| 7 | #define DEBUG_SOMFY Serial | ||
| 8 | |||
| 9 | |||
| 10 | |||
| 11 | |||
| 12 | 3 | [[maybe_unused]] static void SETCHARPROP(char *prop, const char *value, size_t size) {strncpy(prop, value, size); prop[size - 1] = '\0';} | |
| 13 | /* | ||
| 14 | namespace util { | ||
| 15 | // Createa a custom to_string function. C++ can be annoying | ||
| 16 | // with all the trailing 0s on number formats. | ||
| 17 | template <typename T> std::string to_string(const T& t) { | ||
| 18 | std::string str{std::to_string (t)}; | ||
| 19 | int offset{1}; | ||
| 20 | if (str.find_last_not_of('0') == str.find('.')) { | ||
| 21 | offset = 0; | ||
| 22 | } | ||
| 23 | str.erase ( str.find_last_not_of('0') + 1, std::string::npos ); | ||
| 24 | str.erase ( str.find_last_not_of('.') + 1, std::string::npos ); | ||
| 25 | return str; | ||
| 26 | } | ||
| 27 | } | ||
| 28 | */ | ||
| 29 | |||
| 30 | 4 | static void _ltrim(char *str) { | |
| 31 | 4 | int s = 0, j, k = 0; | |
| 32 | 4 | int e = strlen(str); | |
| 33 | 13 | while(s < e && (str[s] == ' ' || str[s] == '\n' || str[s] == '\r' || str[s] == '\t' || str[s] == '"')) s++; | |
| 34 | 4 | if(s > 0) { | |
| 35 | 16 | for(j = s; j < e; j++) { | |
| 36 | 14 | str[k] = str[j]; | |
| 37 | 14 | k++; | |
| 38 | } | ||
| 39 | 2 | str[k] = '\0'; | |
| 40 | } | ||
| 41 | //if(s > 0) strcpy(str, &str[s]); | ||
| 42 | 4 | } | |
| 43 | 619 | static void _rtrim(char *str) { | |
| 44 | 619 | int e = strlen(str) - 1; | |
| 45 | 920 | while(e >= 0 && (str[e] == ' ' || str[e] == '\n' || str[e] == '\r' || str[e] == '\t' || str[e] == '"')) {str[e] = '\0'; e--;} | |
| 46 | 619 | } | |
| 47 | 4 | [[maybe_unused]] static void _trim(char *str) { _ltrim(str); _rtrim(str); } | |
| 48 | struct rebootDelay_t { | ||
| 49 | bool reboot = false; | ||
| 50 | int rebootTime = 0; | ||
| 51 | bool closed = false; | ||
| 52 | }; | ||
| 53 | 12 | [[maybe_unused]] static bool toBoolean(const char *str, bool def) { | |
| 54 | 12 | if(!str) return def; | |
| 55 | 10 | if(strlen(str) == 0) return def; | |
| 56 | 8 | else if(str[0] == 't' || str[0] == 'T' || str[0] == '1') return true; | |
| 57 | 5 | else if(str[0] == 'f' || str[0] == 'F' || str[0] == '0') return false; | |
| 58 | 2 | return def; | |
| 59 | } | ||
| 60 | // Whether a wifi broadcast is worth sending: always on a channel change, | ||
| 61 | // otherwise only for an RSSI change of at least minDelta dBm. | ||
| 62 | bool rssiNeedsEmit(int rssi, int lastRssi, int channel, int lastChannel, int minDelta); | ||
| 63 | // What the last wifi broadcast told every client. A client that connects gets | ||
| 64 | // the signal in its sync, which tells the others nothing, so it isn't moved. | ||
| 65 | struct RssiBroadcast { | ||
| 66 | int rssi = 0; | ||
| 67 | int channel = 0; | ||
| 68 | bool due(int rssi, int channel, int minDelta) const; | ||
| 69 | void sent(int rssi, int channel); | ||
| 70 | }; | ||
| 71 | // Whether an AP a scan found for our SSID is worth connecting to. While | ||
| 72 | // connected it has to be at least minGain dBm stronger than the current AP so | ||
| 73 | // a small swing doesn't make the device hop between APs. | ||
| 74 | bool worthRoaming(bool connected, int currentRssi, int apRssi, int minGain); | ||
| 75 | // Whether the calling task may send socket events. They are all formatted | ||
| 76 | // into one shared buffer, so only loop()'s task may. owner is that task, or | ||
| 77 | // null before ProtoSocket::begin() has recorded it (setup() runs there too). | ||
| 78 | bool emitAllowed(const void *current, const void *owner); | ||
| 79 | // A short name for a wifi_auth_mode_t value, for the logs. The values up to | ||
| 80 | // WAPI are the same in ESP-IDF 4.4 and 5.x. | ||
| 81 | const char *wifiAuthModeName(uint8_t authMode); | ||
| 82 | // Whether a firmware the bootloader is holding for verification after an | ||
| 83 | // update has proven itself: it has run for minUptimeMs and the network is | ||
| 84 | // up. Until then a reset rolls back to the previous firmware. | ||
| 85 | bool otaProven(bool networkUp, unsigned long uptimeMs, unsigned long minUptimeMs); | ||
| 86 | // A release asset's name: "SomfyController.<kind>.esp32<chipModel>.bin", kind | ||
| 87 | // "ino" for the firmware or "littlefs" for the web UI image, chipModel as in | ||
| 88 | // ConfigSettings ("" for the ESP32, "s3", "c3", "c5", "c6", "p4", "p4es"). | ||
| 89 | void releaseAssetName(char *buf, size_t size, const char *kind, const char *chipModel); | ||
| 90 | // A coredump backtrace as " 0x%08x" per frame. bt holds maxFrames entries and | ||
| 91 | // depth is how many the dump claims are valid. | ||
| 92 | void formatBacktrace(char *buf, size_t size, const uint32_t *bt, size_t maxFrames, uint32_t depth); | ||
| 93 | // How well loop() keeps up: the passes it made per second and its longest | ||
| 94 | // pass, over the window since the last memory broadcast. Only the loop | ||
| 95 | // task calls record(), rate(), maxUs() and publish(). publish() closes the | ||
| 96 | // window and keeps its figures for the memory event and a client's sync. | ||
| 97 | class LoopStats { | ||
| 98 | uint32_t _passes = 0; | ||
| 99 | uint32_t _maxUs = 0; | ||
| 100 | uint32_t _startMs = 0; | ||
| 101 | std::atomic<uint32_t> _publishedRate{0}; | ||
| 102 | std::atomic<uint32_t> _publishedMaxUs{0}; | ||
| 103 | public: | ||
| 104 | void record(uint32_t passUs); | ||
| 105 | // Passes per second since the window started, 0 for an empty window. | ||
| 106 | uint32_t rate(uint32_t nowMs) const; | ||
| 107 | 5 | uint32_t maxUs() const { return _maxUs; } | |
| 108 | void publish(uint32_t nowMs); | ||
| 109 | 6 | uint32_t publishedRate() const { return _publishedRate.load(std::memory_order_relaxed); } | |
| 110 | 4 | uint32_t publishedMaxUs() const { return _publishedMaxUs.load(std::memory_order_relaxed); } | |
| 111 | }; | ||
| 112 | |||
| 113 | class Timestamp { | ||
| 114 | char _timeBuffer[128]; | ||
| 115 | public: | ||
| 116 | time_t getUTC(); | ||
| 117 | time_t getUTC(time_t epoch); | ||
| 118 | char * getISOTime(); | ||
| 119 | char * getISOTime(time_t epoch); | ||
| 120 | char * formatISO(struct tm *dt, int tz); | ||
| 121 | int tzOffset(); | ||
| 122 | static time_t parseUTCTime(const char *buff); | ||
| 123 | static time_t mkUTCTime(struct tm *dt); | ||
| 124 | static int calcTZOffset(time_t *dt); | ||
| 125 | static time_t now(); | ||
| 126 | static unsigned long epoch(); | ||
| 127 | }; | ||
| 128 | // Sort an array | ||
| 129 | template<typename AnyType> void sortArray(AnyType array[], size_t sizeOfArray); | ||
| 130 | // Sort in reverse | ||
| 131 | template<typename AnyType> void sortArrayReverse(AnyType array[], size_t sizeOfArray); | ||
| 132 | // Sort an array with custom comparison function | ||
| 133 | template<typename AnyType> void sortArray(AnyType array[], size_t sizeOfArray, bool (*largerThan)(AnyType, AnyType)); | ||
| 134 | // Sort in reverse with custom comparison function | ||
| 135 | template<typename AnyType> void sortArrayReverse(AnyType array[], size_t sizeOfArray, bool (*largerThan)(AnyType, AnyType)); | ||
| 136 | namespace ArduinoSort { | ||
| 137 | 25 | template<typename AnyType> bool builtinLargerThan(AnyType first, AnyType second) { return first > second; } | |
| 138 | //template<> bool builtinLargerThan(char* first, char* second) { return strcmp(first, second) > 0; } | ||
| 139 | 25 | template<typename AnyType> void insertionSort(AnyType array[], size_t sizeOfArray, bool reverse, bool (*largerThan)(AnyType, AnyType)) { for (size_t i = 1; i < sizeOfArray; i++) { | |
| 140 | 43 | for (size_t j = i; j > 0 && (largerThan(array[j-1], array[j]) != reverse); j--) { | |
| 141 | 25 | AnyType tmp = array[j-1]; | |
| 142 | 25 | array[j-1] = array[j]; | |
| 143 | 25 | array[j] = tmp; | |
| 144 | } | ||
| 145 | } | ||
| 146 | 7 | } | |
| 147 | } | ||
| 148 | 4 | template<typename AnyType> void sortArray(AnyType array[], size_t sizeOfArray) { ArduinoSort::insertionSort(array, sizeOfArray, false, ArduinoSort::builtinLargerThan); } | |
| 149 | 1 | template<typename AnyType> void sortArrayReverse(AnyType array[], size_t sizeOfArray) { ArduinoSort::insertionSort(array, sizeOfArray, true, ArduinoSort::builtinLargerThan); } | |
| 150 | 1 | template<typename AnyType> void sortArray(AnyType array[], size_t sizeOfArray, bool (*largerThan)(AnyType, AnyType)) { ArduinoSort::insertionSort(array, sizeOfArray, false, largerThan); } | |
| 151 | 1 | template<typename AnyType> void sortArrayReverse(AnyType array[], size_t sizeOfArray, bool (*largerThan)(AnyType, AnyType)) { ArduinoSort::insertionSort(array, sizeOfArray, true, largerThan); } | |
| 152 | #endif | ||
| 153 |