Utils.cpp
| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | #include <Arduino.h> | ||
| 2 | #include <time.h> | ||
| 3 | #include "Utils.h" | ||
| 4 | |||
| 5 | |||
| 6 | /********************************************************************* | ||
| 7 | * Timestamp class members | ||
| 8 | ********************************************************************/ | ||
| 9 | ✗ | unsigned long Timestamp::epoch() { | |
| 10 | struct tm tmNow; | ||
| 11 | time_t now; | ||
| 12 | ✗ | if(!getLocalTime(&tmNow,50)) return 0; | |
| 13 | ✗ | time(&now); | |
| 14 | ✗ | return now; | |
| 15 | } | ||
| 16 | ✗ | time_t Timestamp::now() { | |
| 17 | struct tm tmNow; | ||
| 18 | ✗ | getLocalTime(&tmNow,50); | |
| 19 | ✗ | return mktime(&tmNow); | |
| 20 | } | ||
| 21 | ✗ | time_t Timestamp::getUTC() { | |
| 22 | time_t t; | ||
| 23 | ✗ | time(&t); | |
| 24 | ✗ | return t; | |
| 25 | } | ||
| 26 | 2 | time_t Timestamp::mkUTCTime(struct tm *dt) { | |
| 27 | 2 | time_t tsBadLocal = mktime(dt); | |
| 28 | |||
| 29 | struct tm tmUTC; | ||
| 30 | struct tm tmLocal; | ||
| 31 | 2 | gmtime_r(&tsBadLocal, &tmUTC); | |
| 32 | 2 | localtime_r(&tsBadLocal, &tmLocal); | |
| 33 | 2 | time_t tsBadUTC = mktime(&tmUTC); | |
| 34 | 2 | time_t tsLocal = mktime(&tmLocal); | |
| 35 | 2 | time_t tsLocalOffset = tsLocal - tsBadUTC; | |
| 36 | 2 | return tsBadLocal + tsLocalOffset; | |
| 37 | } | ||
| 38 | 2 | time_t Timestamp::parseUTCTime(const char *buff) { | |
| 39 | struct tm dt; | ||
| 40 | 2 | dt.tm_hour = 0; | |
| 41 | 2 | dt.tm_mday = 0; | |
| 42 | 2 | dt.tm_mon = 0; | |
| 43 | 2 | dt.tm_year = 0; | |
| 44 | 2 | dt.tm_wday = 0; | |
| 45 | 2 | dt.tm_yday = 0; | |
| 46 | 2 | dt.tm_isdst = false; | |
| 47 | char num[5]; | ||
| 48 | 2 | uint8_t i = 0; | |
| 49 | 2 | memset(num, 0x00, sizeof(num)); | |
| 50 | 10 | for(uint8_t j = 0; j < 5 && i < strlen(buff);) { | |
| 51 | 10 | char ch = buff[i++]; | |
| 52 | 10 | if(ch == '-') break; | |
| 53 | 8 | if(!isdigit(ch)) continue; | |
| 54 | 8 | else num[j++] = ch; | |
| 55 | } | ||
| 56 | 2 | dt.tm_year = atoi(num)-1900; | |
| 57 | 2 | memset(num, 0x00, sizeof(num)); | |
| 58 | 6 | for(uint8_t j = 0; j < 5 && i < strlen(buff);) { | |
| 59 | 6 | char ch = buff[i++]; | |
| 60 | 6 | if(ch == '-') break; | |
| 61 | 4 | if(!isdigit(ch)) continue; | |
| 62 | 4 | else num[j++] = ch; | |
| 63 | } | ||
| 64 | 2 | dt.tm_mon = atoi(num)-1; | |
| 65 | 2 | memset(num, 0x00, sizeof(num)); | |
| 66 | 6 | for(uint8_t j = 0; j < 5 && i < strlen(buff);) { | |
| 67 | 6 | char ch = buff[i++]; | |
| 68 | 6 | if(ch == '-' || ch == 'T' || ch == 't') break; | |
| 69 | 4 | if(!isdigit(ch)) continue; | |
| 70 | 4 | else num[j++] = ch; | |
| 71 | } | ||
| 72 | 2 | dt.tm_mday = atoi(num); | |
| 73 | 2 | memset(num, 0x00, sizeof(num)); | |
| 74 | 6 | for(uint8_t j = 0; j < 5 && i < strlen(buff);) { | |
| 75 | 6 | char ch = buff[i++]; | |
| 76 | 6 | if(ch == '-' || ch == ':') break; | |
| 77 | 4 | if(!isdigit(ch)) continue; | |
| 78 | 4 | else num[j++] = ch; | |
| 79 | } | ||
| 80 | 2 | dt.tm_hour = atoi(num); | |
| 81 | 2 | memset(num, 0x00, sizeof(num)); | |
| 82 | 6 | for(uint8_t j = 0; j < 5 && i < strlen(buff);) { | |
| 83 | 6 | char ch = buff[i++]; | |
| 84 | 6 | if(ch == '-' || ch == ':') break; | |
| 85 | 4 | if(!isdigit(ch)) continue; | |
| 86 | 4 | else num[j++] = ch; | |
| 87 | } | ||
| 88 | 2 | dt.tm_min = atoi(num); | |
| 89 | 6 | for(uint8_t j = 0; j < 5 && i < strlen(buff);) { | |
| 90 | 6 | char ch = buff[i++]; | |
| 91 | 6 | if(ch == '-' || ch == ':' || ch == 'Z') break; | |
| 92 | 4 | if(!isdigit(ch)) continue; | |
| 93 | 4 | else num[j++] = ch; | |
| 94 | } | ||
| 95 | 2 | dt.tm_sec = atoi(num); | |
| 96 | //Serial.printf("Y:%d M:%d D:%d H:%d M:%d S:%d\n", dt.tm_year, dt.tm_mon, dt.tm_mday, dt.tm_hour, dt.tm_min, dt.tm_sec); | ||
| 97 | 2 | return Timestamp::mkUTCTime(&dt); | |
| 98 | } | ||
| 99 | ✗ | time_t Timestamp::getUTC(time_t t) { | |
| 100 | tm tmUTC; | ||
| 101 | ✗ | gmtime_r(&t, &tmUTC); | |
| 102 | ✗ | return mktime(&tmUTC); | |
| 103 | } | ||
| 104 | ✗ | char * Timestamp::getISOTime() { return this->getISOTime(this->getUTC()); } | |
| 105 | ✗ | char * Timestamp::getISOTime(time_t epoch) { | |
| 106 | ✗ | struct tm *dt = localtime((time_t *)&epoch); | |
| 107 | ✗ | return this->formatISO(dt, this->tzOffset()); | |
| 108 | } | ||
| 109 | 3 | char * Timestamp::formatISO(struct tm *dt, int tz) { | |
| 110 | 3 | int tzHrs = floor(tz/100); | |
| 111 | 3 | int tzMin = tz - (tzHrs * 100); | |
| 112 | 3 | int ms = millis() % 1000; | |
| 113 | 3 | snprintf(this->_timeBuffer, sizeof(this->_timeBuffer), "%04d-%02d-%02dT%02d:%02d:%02d.%03d%s%02d%02d", | |
| 114 | 3 | dt->tm_year + 1900, dt->tm_mon + 1, dt->tm_mday, dt->tm_hour, dt->tm_min, dt->tm_sec, ms, tzHrs < 0 ? "-" : "+", abs(tzHrs), abs(tzMin)); | |
| 115 | 3 | return this->_timeBuffer; | |
| 116 | } | ||
| 117 | 4 | int Timestamp::calcTZOffset(time_t *dt) { | |
| 118 | tm tmLocal, tmUTC; | ||
| 119 | 4 | gmtime_r(dt, &tmUTC); | |
| 120 | 4 | localtime_r(dt, &tmLocal); | |
| 121 | 4 | long diff = mktime(&tmLocal) - mktime(&tmUTC); | |
| 122 | 4 | if(tmLocal.tm_isdst) diff += 3600; | |
| 123 | // Encoded as hhmm, e.g. +05:30 is 530 and -03:30 is -330. | ||
| 124 | 4 | int hrs = (int)((diff/3600) * 100); | |
| 125 | 4 | int mins = (int)((diff % 3600) / 60); | |
| 126 | 4 | return hrs + mins; | |
| 127 | } | ||
| 128 | ✗ | int Timestamp::tzOffset() { | |
| 129 | time_t now; | ||
| 130 | ✗ | time(&now); | |
| 131 | ✗ | return Timestamp::calcTZOffset(&now); | |
| 132 | } | ||
| 133 | 9 | bool rssiNeedsEmit(int rssi, int lastRssi, int channel, int lastChannel, int minDelta) { | |
| 134 | 9 | if(channel != lastChannel) return true; | |
| 135 | 6 | int delta = rssi > lastRssi ? rssi - lastRssi : lastRssi - rssi; | |
| 136 | 6 | return delta >= minDelta; | |
| 137 | } | ||
| 138 | 4 | bool RssiBroadcast::due(int rssi, int channel, int minDelta) const { | |
| 139 | 4 | return rssiNeedsEmit(rssi, this->rssi, channel, this->channel, minDelta); | |
| 140 | } | ||
| 141 | 3 | void RssiBroadcast::sent(int rssi, int channel) { | |
| 142 | 3 | this->rssi = rssi; | |
| 143 | 3 | this->channel = channel; | |
| 144 | 3 | } | |
| 145 | 3 | bool emitAllowed(const void *current, const void *owner) { | |
| 146 | 3 | return owner == nullptr || current == owner; | |
| 147 | } | ||
| 148 | 6 | bool worthRoaming(bool connected, int currentRssi, int apRssi, int minGain) { | |
| 149 | 6 | return !connected || apRssi >= currentRssi + minGain; | |
| 150 | } | ||
| 151 | 4 | bool otaProven(bool networkUp, unsigned long uptimeMs, unsigned long minUptimeMs) { | |
| 152 | 4 | return networkUp && uptimeMs >= minUptimeMs; | |
| 153 | } | ||
| 154 | 7 | void releaseAssetName(char *buf, size_t size, const char *kind, const char *chipModel) { | |
| 155 | 7 | snprintf(buf, size, "SomfyController.%s.esp32%s.bin", kind, chipModel); | |
| 156 | 7 | } | |
| 157 | 3 | void formatBacktrace(char *buf, size_t size, const uint32_t *bt, size_t maxFrames, uint32_t depth) { | |
| 158 | // depth comes from flash. A half-written dump reads back as 0xa5a5a5a5, | ||
| 159 | // so never trust it past bt[] or the buffer. | ||
| 160 | 3 | buf[0] = '\0'; | |
| 161 | 3 | size_t pos = 0; | |
| 162 | 3 | size_t frames = depth < maxFrames ? depth : maxFrames; | |
| 163 | 25 | for (size_t i = 0; i < frames && pos < size - 1; i++) { | |
| 164 | 22 | int n = snprintf(buf + pos, size - pos, " 0x%08x", (unsigned int)bt[i]); | |
| 165 | 22 | if (n < 0) break; | |
| 166 | 22 | pos += (size_t)n; | |
| 167 | } | ||
| 168 | 3 | } | |
| 169 | 7 | const char *wifiAuthModeName(uint8_t authMode) { | |
| 170 | 7 | switch(authMode) { | |
| 171 | 1 | case 0: return "OPEN"; | |
| 172 | ✗ | case 1: return "WEP"; | |
| 173 | ✗ | case 2: return "WPA"; | |
| 174 | 1 | case 3: return "WPA2"; | |
| 175 | ✗ | case 4: return "WPA/WPA2"; | |
| 176 | ✗ | case 5: return "WPA2-ENT"; | |
| 177 | 1 | case 6: return "WPA3"; | |
| 178 | 1 | case 7: return "WPA2/WPA3"; | |
| 179 | 1 | case 8: return "WAPI"; | |
| 180 | 2 | default: return "UNKNOWN"; | |
| 181 | } | ||
| 182 | } | ||
| 183 | 5882 | void LoopStats::record(uint32_t passUs) { | |
| 184 | 5882 | _passes++; | |
| 185 | 5882 | if(passUs > _maxUs) _maxUs = passUs; | |
| 186 | 5882 | } | |
| 187 | 9 | uint32_t LoopStats::rate(uint32_t nowMs) const { | |
| 188 | // The unsigned subtraction keeps the window right across a millis() wrap. | ||
| 189 | 9 | const uint32_t elapsedMs = nowMs - _startMs; | |
| 190 | 9 | if(elapsedMs == 0) return 0; | |
| 191 | 7 | return (uint32_t)((uint64_t)_passes * 1000 / elapsedMs); | |
| 192 | } | ||
| 193 | 2 | void LoopStats::publish(uint32_t nowMs) { | |
| 194 | 2 | _publishedRate.store(this->rate(nowMs), std::memory_order_relaxed); | |
| 195 | 2 | _publishedMaxUs.store(_maxUs, std::memory_order_relaxed); | |
| 196 | 2 | _passes = 0; | |
| 197 | 2 | _maxUs = 0; | |
| 198 | 2 | _startMs = nowMs; | |
| 199 | 2 | } | |
| 200 |