diff --git a/.github/workflows/compile_development.yml b/.github/workflows/compile_development.yml index b5e0e0e1..223a7a39 100644 --- a/.github/workflows/compile_development.yml +++ b/.github/workflows/compile_development.yml @@ -25,6 +25,8 @@ jobs: variant: - opendtufusion - opendtufusion-ethernet + - opendtufusion-16MB + - opendtufusion-ethernet-16MB - esp8266 - esp8266-all - esp8266-minimal @@ -94,6 +96,8 @@ jobs: variant: - opendtufusion-de - opendtufusion-ethernet-de + - opendtufusion-16MB-de + - opendtufusion-ethernet-16MB-de - esp8266-de - esp8266-all-de - esp8266-prometheus-de diff --git a/src/CHANGES.md b/src/CHANGES.md index f4c5d4ce..c60bd5c4 100644 --- a/src/CHANGES.md +++ b/src/CHANGES.md @@ -1,10 +1,35 @@ # Development Changes +## 0.8.129 - 2024-07-11 +* sort alarms ascending #1471 +* fix alarm counter for first alarm +* prevent add inverter multiple times #1700 + +## 0.8.128 - 2024-07-10 +* add environments for 16MB flash size ESP32-S3 aka opendtufusion +* prevent duplicate alarms, update end time once it is received #1471 + +## 0.8.127 - 2024-06-21 +* add grid file #1677 +* merge PR: Bugfix Inv delete not working with password protection #1678 + +## 0.8.126 - 2024-06-12 +* merge PR: Update pubMqtt.h - Bugfix #1673 #1674 + +## 0.8.125 - 2024-06-09 +* fix ESP8266 compilation +* merge PR: active_PowerLimit #1663 + +## 0.8.124 - 2024-06-06 +* improved MqTT `OnMessage` (threadsafe) +* support of HERF inverters, serial number is converted in Javascript #1425 +* revert buffer size in `RestAPI` for ESP8266 #1650 + ## 0.8.123 - 2024-05-30 * fix ESP8266, ESP32 static IP #1643 #1608 * update MqTT library which enhances stability #1646 -* merge PR: MQTT JSON Payload pro Kanal und total, auswählbar #1541 -* add option to publish mqtt as json +* merge PR: MqTT JSON Payload pro Kanal und total, auswählbar #1541 +* add option to publish MqTT as json * publish rssi not on ch0 any more, published on `topic/rssi` * add total power to index page (if multiple inverters are configured) * show device name in html title #1639 diff --git a/src/config/config.h b/src/config/config.h index 49705eb6..7a3dbb64 100644 --- a/src/config/config.h +++ b/src/config/config.h @@ -16,9 +16,8 @@ //------------------------------------- // Fallback WiFi Info -#define FB_WIFI_SSID "YOUR_WIFI_SSID" -#define FB_WIFI_PWD "YOUR_WIFI_PWD" - +#define FB_WIFI_SSID "" +#define FB_WIFI_PWD "" // Access Point Info // In case there is no WiFi Network or Ahoy can not connect to it, it will act as an Access Point diff --git a/src/config/config_override_example.h b/src/config/config_override_example.h index 44623c1f..a84c1e8a 100644 --- a/src/config/config_override_example.h +++ b/src/config/config_override_example.h @@ -6,9 +6,6 @@ #ifndef __CONFIG_OVERRIDE_H__ #define __CONFIG_OVERRIDE_H__ -// override fallback WiFi info -#define FB_WIFI_OVERRIDDEN - // each override must be preceded with an #undef statement #undef FB_WIFI_SSID #define FB_WIFI_SSID "MY_SSID" diff --git a/src/defines.h b/src/defines.h index ab651821..5914daa2 100644 --- a/src/defines.h +++ b/src/defines.h @@ -13,7 +13,7 @@ //------------------------------------- #define VERSION_MAJOR 0 #define VERSION_MINOR 8 -#define VERSION_PATCH 123 +#define VERSION_PATCH 129 //------------------------------------- typedef struct { uint8_t ch; diff --git a/src/hm/hmInverter.h b/src/hm/hmInverter.h index b72ec5ab..ff3ff9c5 100644 --- a/src/hm/hmInverter.h +++ b/src/hm/hmInverter.h @@ -670,7 +670,6 @@ class Inverter { DPRINTLN(DBG_DEBUG, "Alarm #" + String(pyld[startOff+1]) + " '" + String(getAlarmStr(pyld[startOff+1])) + "' start: " + ah::getTimeStr(start) + ", end: " + ah::getTimeStr(endTime)); addAlarm(pyld[startOff+1], start, endTime); - alarmCnt++; alarmLastId = alarmMesIndex; return pyld[startOff+1]; @@ -818,6 +817,26 @@ class Inverter { private: inline void addAlarm(uint16_t code, uint32_t start, uint32_t end) { + bool found = false; + uint8_t i = 0; + + if(start > end) + end = 0; + + for(; i < 10; i++) { + mAlarmNxtWrPos = (++mAlarmNxtWrPos) % 10; + + if(lastAlarm[mAlarmNxtWrPos].code == code && lastAlarm[mAlarmNxtWrPos].start == start) { + // replace with same or update end time + if(lastAlarm[mAlarmNxtWrPos].end == 0 || lastAlarm[mAlarmNxtWrPos].end == end) { + break; + } + } + } + + if(alarmCnt < 10 && alarmCnt <= mAlarmNxtWrPos) + alarmCnt = mAlarmNxtWrPos + 1; + lastAlarm[mAlarmNxtWrPos] = alarm_t(code, start, end); if(++mAlarmNxtWrPos >= 10) // rolling buffer mAlarmNxtWrPos = 0; diff --git a/src/network/AhoyWifiEsp32.h b/src/network/AhoyWifiEsp32.h index 9337dc47..aff2071a 100644 --- a/src/network/AhoyWifiEsp32.h +++ b/src/network/AhoyWifiEsp32.h @@ -17,7 +17,7 @@ class AhoyWifi : public AhoyNetwork { virtual void begin() override { mAp.enable(); - if(String(FB_WIFI_SSID) == mConfig->sys.stationSsid) + if(strlen(mConfig->sys.stationSsid) == 0) return; // no station wifi defined WiFi.disconnect(); // clean up diff --git a/src/platformio.ini b/src/platformio.ini index 69359aa4..ffdbd1f7 100644 --- a/src/platformio.ini +++ b/src/platformio.ini @@ -30,7 +30,7 @@ lib_deps = https://github.com/nRF24/RF24.git#v1.4.8 paulstoffregen/Time @ ^1.6.1 https://github.com/bertmelis/espMqttClient#v1.7.0 - bblanchon/ArduinoJson @ ^6.21.3 + bblanchon/ArduinoJson @ ^6.21.5 https://github.com/JChristensen/Timezone @ ^1.2.4 olikraus/U8g2 @ ^2.35.9 https://github.com/zinggjm/GxEPD2#1.5.3 @@ -149,7 +149,7 @@ monitor_filters = esp8266_exception_decoder [env:esp32-wroom32-minimal] -platform = espressif32@6.6.0 +platform = espressif32@6.7.0 board = lolin_d32 build_flags = ${env.build_flags} -DSPI_HAL @@ -157,7 +157,7 @@ monitor_filters = esp32_exception_decoder [env:esp32-wroom32] -platform = espressif32@6.6.0 +platform = espressif32@6.7.0 board = lolin_d32 build_flags = ${env:esp32-wroom32-minimal.build_flags} -DUSE_HSPI_FOR_EPD @@ -168,7 +168,7 @@ monitor_filters = esp32_exception_decoder [env:esp32-wroom32-de] -platform = espressif32@6.6.0 +platform = espressif32@6.7.0 board = lolin_d32 build_flags = ${env:esp32-wroom32.build_flags} -DLANG_DE @@ -176,7 +176,7 @@ monitor_filters = esp32_exception_decoder [env:esp32-wroom32-prometheus] -platform = espressif32@6.6.0 +platform = espressif32@6.7.0 board = lolin_d32 build_flags = ${env:esp32-wroom32.build_flags} -DENABLE_PROMETHEUS_EP @@ -184,7 +184,7 @@ monitor_filters = esp32_exception_decoder [env:esp32-wroom32-prometheus-de] -platform = espressif32@6.6.0 +platform = espressif32@6.7.0 board = lolin_d32 build_flags = ${env:esp32-wroom32-prometheus.build_flags} -DLANG_DE @@ -220,7 +220,7 @@ monitor_filters = esp32_exception_decoder [env:esp32-s2-mini] -platform = espressif32@6.6.0 +platform = espressif32@6.7.0 board = lolin_s2_mini build_flags = ${env.build_flags} -DUSE_HSPI_FOR_EPD @@ -243,7 +243,7 @@ monitor_filters = esp32_exception_decoder [env:esp32-s2-mini-de] -platform = espressif32@6.6.0 +platform = espressif32@6.7.0 board = lolin_s2_mini build_flags = ${env:esp32-s2-mini.build_flags} -DLANG_DE @@ -251,7 +251,7 @@ monitor_filters = esp32_exception_decoder [env:esp32-c3-mini] -platform = espressif32@6.6.0 +platform = espressif32@6.7.0 board = lolin_c3_mini build_flags = ${env.build_flags} -DUSE_HSPI_FOR_EPD @@ -274,7 +274,7 @@ monitor_filters = esp32_exception_decoder [env:esp32-c3-mini-de] -platform = espressif32@6.6.0 +platform = espressif32@6.7.0 board = lolin_c3_mini build_flags = ${env:esp32-c3-mini.build_flags} -DLANG_DE @@ -282,7 +282,7 @@ monitor_filters = esp32_exception_decoder [env:opendtufusion-minimal] -platform = espressif32@6.6.0 +platform = espressif32@6.7.0 board = esp32-s3-devkitc-1 upload_protocol = esp-builtin build_flags = ${env.build_flags} @@ -307,7 +307,7 @@ monitor_filters = esp32_exception_decoder, colorize [env:opendtufusion] -platform = espressif32@6.6.0 +platform = espressif32@6.7.0 board = esp32-s3-devkitc-1 upload_protocol = esp-builtin build_flags = ${env:opendtufusion-minimal.build_flags} @@ -318,7 +318,7 @@ monitor_filters = esp32_exception_decoder, colorize [env:opendtufusion-de] -platform = espressif32@6.6.0 +platform = espressif32@6.7.0 board = esp32-s3-devkitc-1 upload_protocol = esp-builtin build_flags = ${env:opendtufusion.build_flags} @@ -327,7 +327,7 @@ monitor_filters = esp32_exception_decoder, colorize [env:opendtufusion-ethernet] -platform = espressif32@6.6.0 +platform = espressif32@6.7.0 board = esp32-s3-devkitc-1 upload_protocol = esp-builtin build_flags = ${env:opendtufusion-minimal.build_flags} @@ -346,10 +346,46 @@ monitor_filters = esp32_exception_decoder, colorize [env:opendtufusion-ethernet-de] -platform = espressif32@6.6.0 +platform = espressif32@6.7.0 board = esp32-s3-devkitc-1 upload_protocol = esp-builtin build_flags = ${env:opendtufusion-ethernet.build_flags} -DLANG_DE monitor_filters = esp32_exception_decoder, colorize + +[env:opendtufusion-16MB] +platform = espressif32@6.7.0 +board = esp32-s3-devkitc-1 +board_upload.flash_size = 16MB +upload_protocol = esp-builtin +build_flags = ${env:opendtufusion.build_flags} +monitor_filters = + esp32_exception_decoder, colorize + +[env:opendtufusion-16MB-de] +platform = espressif32@6.7.0 +board = esp32-s3-devkitc-1 +upload_protocol = esp-builtin +build_flags = ${env:opendtufusion-16MB.build_flags} + -DLANG_DE +monitor_filters = + esp32_exception_decoder, colorize + +[env:opendtufusion-ethernet-16MB] +platform = espressif32@6.7.0 +board = esp32-s3-devkitc-1 +board_upload.flash_size = 16MB +upload_protocol = esp-builtin +build_flags = ${env:opendtufusion-ethernet.build_flags} +monitor_filters = + esp32_exception_decoder, colorize + +[env:opendtufusion-ethernet-16MB-de] +platform = espressif32@6.7.0 +board = esp32-s3-devkitc-1 +upload_protocol = esp-builtin +build_flags = ${env:opendtufusion-ethernet-16MB.build_flags} + -DLANG_DE +monitor_filters = + esp32_exception_decoder, colorize diff --git a/src/plugins/MaxPower.h b/src/plugins/MaxPower.h index a8db6909..68665d61 100644 --- a/src/plugins/MaxPower.h +++ b/src/plugins/MaxPower.h @@ -50,7 +50,7 @@ class MaxPower { if((mValues[i].first + mMaxDiff) >= *mTs) val += mValues[i].second; else if(mValues[i].first > 0) - return mLast; // old data + break; // old data } if(val > mLast) mLast = val; diff --git a/src/publisher/pubMqtt.h b/src/publisher/pubMqtt.h index f092a159..28cfb358 100644 --- a/src/publisher/pubMqtt.h +++ b/src/publisher/pubMqtt.h @@ -11,6 +11,8 @@ #if defined(ENABLE_MQTT) #ifdef ESP8266 #include + #define xSemaphoreTake(a, b) { while(a) { yield(); } a = true; } + #define xSemaphoreGive(a) { a = false; } #elif defined(ESP32) #include #endif @@ -39,6 +41,13 @@ template class PubMqtt { public: PubMqtt() : SendIvData() { + #if defined(ESP32) + mutex = xSemaphoreCreateBinaryStatic(&mutexBuffer); + xSemaphoreGive(mutex); + #else + mutex = false; + #endif + mLastIvState.fill(InverterStatus::OFF); mIvLastRTRpub.fill(0); @@ -50,7 +59,11 @@ class PubMqtt { mSendAlarm.fill(false); } - ~PubMqtt() { } + ~PubMqtt() { + #if defined(ESP32) + vSemaphoreDelete(mutex); + #endif + } void setup(IApp *app, cfgMqtt_t *cfg_mqtt, const char *devName, const char *version, HMSYSTEM *sys, uint32_t *utcTs, uint32_t *uptime) { mApp = app; @@ -96,6 +109,17 @@ class PubMqtt { } void loop() { + std::queue queue; + xSemaphoreTake(mutex, portMAX_DELAY); + queue.swap(mReceiveQueue); + xSemaphoreGive(mutex); + + while (!queue.empty()) { + message_s *entry = &queue.front(); + handleMessage(entry->topic, entry->payload, entry->len, entry->index, entry->total); + queue.pop(); + } + SendIvData.loop(); #if defined(ESP8266) @@ -301,6 +325,14 @@ class PubMqtt { void onMessage(const espMqttClientTypes::MessageProperties& properties, const char* topic, const uint8_t* payload, size_t len, size_t index, size_t total) { if(len == 0) return; + + xSemaphoreTake(mutex, portMAX_DELAY); + mReceiveQueue.push(message_s(topic, payload, len, index, total)); + xSemaphoreGive(mutex); + + } + + inline void handleMessage(const char* topic, const uint8_t* payload, size_t len, size_t index, size_t total) { DPRINT(DBG_INFO, mqttStr[MQTT_STR_GOT_TOPIC]); DBGPRINTLN(String(topic)); if(NULL == mSubscriptionCb) @@ -613,12 +645,77 @@ class PubMqtt { private: enum {MQTT_STATUS_OFFLINE = 0, MQTT_STATUS_PARTIAL, MQTT_STATUS_ONLINE}; + struct message_s + { + char *topic; + uint8_t *payload; + size_t len; + size_t index; + size_t total; + + message_s() + : topic { nullptr } + , payload { nullptr } + , len { 0 } + , index { 0 } + , total { 0 } + {} + + message_s(const char* topic, const uint8_t* payload, size_t len, size_t index, size_t total) + { + uint8_t topic_len = strlen(topic) + 1; + this->topic = new char[topic_len]; + this->payload = new uint8_t[len]; + + memcpy(this->topic, topic, topic_len); + memcpy(this->payload, payload, len); + this->len = len; + this->index = index; + this->total = total; + } + + message_s(const message_s &) = delete; + + message_s(message_s && other) : message_s {} + { + this->swap( other ); + } + + ~message_s() + { + delete[] this->topic; + delete[] this->payload; + } + + message_s &operator = (const message_s &) = delete; + + message_s &operator = (message_s &&other) + { + this->swap(other); + return *this; + } + + void swap(message_s &other) + { + std::swap(this->topic, other.topic); + std::swap(this->payload, other.payload); + std::swap(this->len, other.len); + std::swap(this->index, other.index); + std::swap(this->total, other.total); + } + + }; + private: espMqttClient mClient; cfgMqtt_t *mCfgMqtt = nullptr; IApp *mApp; #if defined(ESP8266) WiFiEventHandler mHWifiCon, mHWifiDiscon; + volatile bool mutex; + #else + SemaphoreHandle_t mutex; + StaticSemaphore_t mutexBuffer; #endif HMSYSTEM *mSys = nullptr; @@ -634,6 +731,8 @@ class PubMqtt { std::array mIvLastRTRpub; uint16_t mIntervalTimeout = 0; + std::queue mReceiveQueue; + // last will topic and payload must be available through lifetime of 'espMqttClient' std::array mLwtTopic; const char *mDevName = nullptr, *mVersion = nullptr; diff --git a/src/publisher/pubMqttIvData.h b/src/publisher/pubMqttIvData.h index 3f12afc6..7965cde6 100644 --- a/src/publisher/pubMqttIvData.h +++ b/src/publisher/pubMqttIvData.h @@ -197,13 +197,20 @@ class PubMqttIvData { if (!mCfg->json) { snprintf(mSubTopic.data(), mSubTopic.size(), "%s/ch%d/%s", mIv->config->name, rec->assign[mPos].ch, fields[rec->assign[mPos].fieldId]); snprintf(mVal.data(), mVal.size(), "%g", ah::round3(mIv->getValue(mPos, rec))); + } else { + if (FLD_ACT_ACTIVE_PWR_LIMIT == rec->assign[mPos].fieldId) { + uint8_t qos = (FLD_ACT_ACTIVE_PWR_LIMIT == rec->assign[mPos].fieldId) ? QOS_2 : QOS_0; + snprintf(mSubTopic.data(), mSubTopic.size(), "%s/%s", mIv->config->name, fields[rec->assign[mPos].fieldId]); + snprintf(mVal.data(), mVal.size(), "%g", ah::round3(mIv->getValue(mPos, rec))); + mPublish(mSubTopic.data(), mVal.data(), retained, qos); + } } } - if ((InverterDevInform_All == mCmd) || (InverterDevInform_Simple == mCmd) || !mCfg->json) { + if ((InverterDevInform_All == mCmd) || (InverterDevInform_Simple == mCmd) || !mCfg->json) + { uint8_t qos = (FLD_ACT_ACTIVE_PWR_LIMIT == rec->assign[mPos].fieldId) ? QOS_2 : QOS_0; - if((FLD_EVT != rec->assign[mPos].fieldId) - && (FLD_LAST_ALARM_CODE != rec->assign[mPos].fieldId)) + if((FLD_EVT != rec->assign[mPos].fieldId) && (FLD_LAST_ALARM_CODE != rec->assign[mPos].fieldId)) mPublish(mSubTopic.data(), mVal.data(), retained, qos); } } diff --git a/src/web/RestApi.h b/src/web/RestApi.h index 0d27c165..345d542b 100644 --- a/src/web/RestApi.h +++ b/src/web/RestApi.h @@ -83,7 +83,11 @@ class RestApi { mHeapFrag = ESP.getHeapFragmentation(); #endif + #if defined(ESP32) AsyncJsonResponse* response = new AsyncJsonResponse(false, 8000); + #else + AsyncJsonResponse* response = new AsyncJsonResponse(false, 6000); + #endif JsonObject root = response->getRoot(); String path = request->url().substring(5); @@ -692,11 +696,23 @@ class RestApi { obj[F("last_id")] = iv->getChannelFieldValue(CH0, FLD_EVT, rec); JsonArray alarm = obj.createNestedArray(F("alarm")); + + // find oldest alarm + uint8_t offset = 0; + uint32_t oldestStart = 0xffffffff; for(uint8_t i = 0; i < 10; i++) { - alarm[i][F("code")] = iv->lastAlarm[i].code; - alarm[i][F("str")] = iv->getAlarmStr(iv->lastAlarm[i].code); - alarm[i][F("start")] = iv->lastAlarm[i].start; - alarm[i][F("end")] = iv->lastAlarm[i].end; + if((iv->lastAlarm[i].start != 0) && (iv->lastAlarm[i].start < oldestStart)) { + offset = i; + oldestStart = iv->lastAlarm[i].start; + } + } + + for(uint8_t i = 0; i < 10; i++) { + uint8_t pos = (i + offset) % 10; + alarm[pos][F("code")] = iv->lastAlarm[pos].code; + alarm[pos][F("str")] = iv->getAlarmStr(iv->lastAlarm[pos].code); + alarm[pos][F("start")] = iv->lastAlarm[pos].start; + alarm[pos][F("end")] = iv->lastAlarm[pos].end; } } @@ -1086,7 +1102,19 @@ class RestApi { mApp->saveSettings(true); } else if(F("save_iv") == jsonIn[F("cmd")]) { - Inverter<> *iv = mSys->getInverterByPos(jsonIn[F("id")], false); + Inverter<> *iv; + + for(uint8_t i = 0; i < MAX_NUM_INVERTERS; i++) { + iv = mSys->getInverterByPos(jsonIn[F("id")], true); + if(nullptr != iv) { + if((i != jsonIn[F("id")]) && (iv->config->serial.u64 == jsonIn[F("ser")])) { + jsonOut[F("error")] = F("ERR_DUPLICATE_INVERTER"); + return false; + } + } + } + + iv = mSys->getInverterByPos(jsonIn[F("id")], false); iv->config->enabled = jsonIn[F("en")]; iv->config->serial.u64 = jsonIn[F("ser")]; snprintf(iv->config->name, MAX_NAME_LENGTH, "%s", jsonIn[F("name")].as()); diff --git a/src/web/html/grid_info.json b/src/web/html/grid_info.json index efb5651d..5b5d4186 100644 --- a/src/web/html/grid_info.json +++ b/src/web/html/grid_info.json @@ -10,6 +10,7 @@ {"0x0908": "France_VFR2014"}, {"0x0a00": "DE NF_EN_50549-1:2019"}, {"0x0c00": "AT_TOR_Erzeuger_default"}, + {"0x0c03": "AT_TOR_Erzeuger_cosphi=1"}, {"0x0c04": "AT_TOR_Erzeuger_default"}, {"0x0d00": "FR_VFR2019"}, {"0x0d04": "NF_EN_50549-1:2019"}, diff --git a/src/web/html/index.html b/src/web/html/index.html index 442160d4..ee78ac8e 100644 --- a/src/web/html/index.html +++ b/src/web/html/index.html @@ -116,6 +116,8 @@ var p = div(["none"]); var total = 0; var count = 0; + var mobile = window.screen.width < 470; + for(var i of obj) { var icon = iconSuccess; var cl = "icon-success"; @@ -131,7 +133,8 @@ } else if(0 == i["ts_last_success"]) { avail = "{#AVAIL_NO_DATA}"; } else { - avail = "{#AVAIL} "; + if (!mobile) + avail = "{#AVAIL} "; if(false == i["is_producing"]) avail += "{#NOT_PRODUCING}"; else { @@ -142,11 +145,16 @@ } } + var text; + if (mobile) + text = "#"; + else + text = "{#INVERTER} #"; p.append( - svg(icon, 30, 30, "icon " + cl), - span("{#INVERTER} #" + i["id"] + ": " + i["name"] + " {#IS} " + avail), - br() - ); + svg(icon, 30, 30, "icon " + cl), + span(text + i["id"] + ": " + i["name"] + " {#IS} " + avail), + br() + ); if(false == i["is_avail"]) { if(i["ts_last_success"] > 0) { diff --git a/src/web/html/setup.html b/src/web/html/setup.html index 13f18061..40e5efb7 100644 --- a/src/web/html/setup.html +++ b/src/web/html/setup.html @@ -873,11 +873,16 @@ ser.dispatchEvent(new Event('change')); function ivSave() { - var o = new Object(); + var o = {} o.cmd = "save_iv" o.token = "*" o.id = obj.id - o.ser = parseInt(document.getElementsByName("ser")[0].value, 16); + + let sn = document.getElementsByName("ser")[0].value + if(sn[0] == 'A') + sn = convHerf(sn) + o.ser = parseInt(sn, 16) + o.name = document.getElementsByName("name")[0].value; o.en = document.getElementsByName("enable")[0].checked; o.ch = []; @@ -897,6 +902,30 @@ getAjax("/api/setup", cb, "POST", JSON.stringify(o)); } + function convHerf(sn) { + let sn_int = 0n; + const CHARS = "0123456789ABCDEFGHJKLMNPRSTUVWXY"; + + for (let i = 0; i < 9; ++i) { + const pos = CHARS.indexOf(sn[i]) + const shift = 42 - 5 * i - (i <= 2 ? 0 : 2) + sn_int |= BigInt(pos) << BigInt(shift) + } + + let first4Hex = (sn_int >> 32n) & 0xFFFFn + + if (first4Hex === 0x2841n) + first4Hex = 0x1121n + else if (first4Hex === 0x2821n) + first4Hex = 0x1141n + else if (first4Hex === 0x2801n) + first4Hex = 0x1161n + + sn_int = (sn_int & ~(0xFFFFn << 32n)) | (first4Hex << 32n); + + return sn_int.toString(16) + } + function cb(obj2) { var e = document.getElementById("res"); if(!obj2.success) @@ -918,6 +947,7 @@ function del() { var o = new Object(); o.cmd = "save_iv"; + o.token = "*" o.id = obj.id; o.ser = 0; o.name = ""; diff --git a/src/web/html/visualization.html b/src/web/html/visualization.html index 0fdaf89e..507a6f9f 100644 --- a/src/web/html/visualization.html +++ b/src/web/html/visualization.html @@ -276,7 +276,7 @@ ml("div", {class: "col mt-3"}, String(a.str)), ml("div", {class: "col mt-3"}, String(a.code)), ml("div", {class: "col mt-3"}, String(toIsoTimeStr(new Date((a.start + offs) * 1000)))), - ml("div", {class: "col mt-3"}, String(toIsoTimeStr(new Date((a.end + offs) * 1000)))) + ml("div", {class: "col mt-3"}, (a.end == 0) ? "-" : String(toIsoTimeStr(new Date((a.end + offs) * 1000)))) ]) ); }