From 512d7f95b477b4c3e58ea0b8a548cea73ec37872 Mon Sep 17 00:00:00 2001 From: Frank Haverland Date: Sat, 11 Mar 2023 11:19:29 +0100 Subject: [PATCH 01/87] Testcase for #2145 and debug-log (#2151) * new models ana-cont-11.0.5, ana-class100-1.5.7, dig-class100-1.6.0 * Testcase for #2145 Added debug log, if allowNegativeRates is handeled --- .../ClassFlowPostProcessing.cpp | 4 ++- .../test_flow_pp_negative.cpp | 27 ++++++++++++++++++ code/test/test_suite_flowcontroll.cpp | 8 +++--- sd-card/config/dig-class100_0160_s2_q.tflite | Bin 0 -> 226480 bytes 4 files changed, 34 insertions(+), 5 deletions(-) create mode 100644 sd-card/config/dig-class100_0160_s2_q.tflite diff --git a/code/components/jomjol_flowcontroll/ClassFlowPostProcessing.cpp b/code/components/jomjol_flowcontroll/ClassFlowPostProcessing.cpp index 22e4ff72..e9abf7c9 100644 --- a/code/components/jomjol_flowcontroll/ClassFlowPostProcessing.cpp +++ b/code/components/jomjol_flowcontroll/ClassFlowPostProcessing.cpp @@ -402,7 +402,7 @@ void ClassFlowPostProcessing::handleAllowNegativeRate(string _decsep, string _va _digit = _decsep.substr(0, _pospunkt); else _digit = "default"; - + for (int j = 0; j < NUMBERS.size(); ++j) { bool _rt = false; @@ -841,6 +841,7 @@ bool ClassFlowPostProcessing::doFlow(string zwtime) if (!NUMBERS[j]->AllowNegativeRates) { + LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "handleAllowNegativeRate for device: " + NUMBERS[j]->name); if ((NUMBERS[j]->Value < NUMBERS[j]->PreValue)) { #ifdef SERIAL_DEBUG @@ -848,6 +849,7 @@ bool ClassFlowPostProcessing::doFlow(string zwtime) NUMBERS[j]->PreValue-(2/pow(10, NUMBERS[j]->Nachkomma)) ) ; #endif + // Include inaccuracy of 0.2 for isExtendedResolution. if (NUMBERS[j]->Value >= (NUMBERS[j]->PreValue-(2/pow(10, NUMBERS[j]->Nachkomma))) && NUMBERS[j]->isExtendedResolution) { NUMBERS[j]->Value = NUMBERS[j]->PreValue; diff --git a/code/test/components/jomjol-flowcontroll/test_flow_pp_negative.cpp b/code/test/components/jomjol-flowcontroll/test_flow_pp_negative.cpp index 87ee56b2..5af4c766 100644 --- a/code/test/components/jomjol-flowcontroll/test_flow_pp_negative.cpp +++ b/code/test/components/jomjol-flowcontroll/test_flow_pp_negative.cpp @@ -76,3 +76,30 @@ void testNegative() { } +/** + * @brief Fehlerberichte aus Issues + * + */ +void testNegative_Issues() { + // Ohne decimal_shift + std::vector digits = { 2.0, 2.0, 0.0, 1.0, 7.2, 9.0, 8.0}; + std::vector analogs = { }; + double preValue_extended = 22018.080; + double preValue = 22018.08; + + const char* expected = "22017.98"; + + // https://github.com/jomjol/AI-on-the-edge-device/issues/2145#issuecomment-1461899094 + // extendResolution=false + // value < preValue + // Prüfung eingeschaltet => Fehler + preValue = 22018.08; // zu groß + UnderTestPost* underTestPost = init_do_flow(analogs, digits, Digital100, false, false, -2); + setAllowNegatives(underTestPost, false); + setPreValue(underTestPost, preValue_extended); + std::string result = process_doFlow(underTestPost); + TEST_ASSERT_EQUAL_STRING("Neg. Rate - Read: - Raw: 22017.98 - Pre: 22018.08 ", underTestPost->getReadoutError().c_str()); + TEST_ASSERT_EQUAL_STRING(expected, result.c_str()); + delete underTestPost; + +} \ No newline at end of file diff --git a/code/test/test_suite_flowcontroll.cpp b/code/test/test_suite_flowcontroll.cpp index 93a459a0..8c36cc1a 100644 --- a/code/test/test_suite_flowcontroll.cpp +++ b/code/test/test_suite_flowcontroll.cpp @@ -108,9 +108,9 @@ extern "C" void app_main() esp_log_level_set("*", ESP_LOG_DEBUG); // set all components to ERROR level UNITY_BEGIN(); - - RUN_TEST(testNegative); - + RUN_TEST(testNegative_Issues); + /* RUN_TEST(testNegative); + RUN_TEST(test_analogToDigit_Standard); RUN_TEST(test_analogToDigit_Transition); RUN_TEST(test_doFlowPP); @@ -121,6 +121,6 @@ extern "C" void app_main() // getReadoutRawString test RUN_TEST(test_getReadoutRawString); - + */ UNITY_END(); } diff --git a/sd-card/config/dig-class100_0160_s2_q.tflite b/sd-card/config/dig-class100_0160_s2_q.tflite new file mode 100644 index 0000000000000000000000000000000000000000..a1769322b84a366b85ae51bcd25a738e4c547fff GIT binary patch literal 226480 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

@KV@+XxtB^>-20vA&9fR_M5f=DeZoCHE>Cz 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 q=cS!*?mh7Q|Ag=^dH$7suf2I-S;EJJogZxfU Date: Sun, 12 Mar 2023 17:01:04 +0100 Subject: [PATCH 02/87] Fix timezone config parser (#2169) * make sure to parse the whole config line * fix crash on empty timezone parameter --------- Co-authored-by: CaCO3 --- code/components/jomjol_time_sntp/time_sntp.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/code/components/jomjol_time_sntp/time_sntp.cpp b/code/components/jomjol_time_sntp/time_sntp.cpp index 8a92c2c0..78df9d5b 100644 --- a/code/components/jomjol_time_sntp/time_sntp.cpp +++ b/code/components/jomjol_time_sntp/time_sntp.cpp @@ -175,10 +175,15 @@ bool setupTime() { while (configFile.getNextLine(&line, disabledLine, eof) && !configFile.isNewParagraph(line)) { - splitted = ZerlegeZeile(line); + splitted = ZerlegeZeile(line, "="); if (toUpper(splitted[0]) == "TIMEZONE") { - timeZone = splitted[1]; + if (splitted.size() <= 1) { // parameter part is empty + timeZone = ""; + } + else { + timeZone = splitted[1]; + } } if (toUpper(splitted[0]) == "TIMESERVER") { From ff81fcbd7f30904e9160474a6b442d746381a99e Mon Sep 17 00:00:00 2001 From: CaCO3 Date: Sun, 12 Mar 2023 17:03:03 +0100 Subject: [PATCH 03/87] Enhance ROI pages (#2161) * Check if the ROIs are equidistant. Only if not, untick the checkbox * renaming * Check if the ROIs have same y, dy and dx. If so, tick the sync checkbox * only allow editing space when box is checked * fix sync check * show inner frame on all ROIs * cleanup * Check if the ROIs have same dy and dx. If so, tick the sync checkbox * checkbox position * renaming * renaming * show inner frame and cross hairs on all ROIs * update ROIs on ticking checkboxes * show timezone hint * fix deleting last ROI * cleanup --------- Co-authored-by: CaCO3 --- sd-card/html/common.js | 2 +- sd-card/html/edit_analog.html | 81 ++++++++++++++++------ sd-card/html/edit_config_param.html | 1 + sd-card/html/edit_digits.html | 102 +++++++++++++++++++++++----- 4 files changed, 146 insertions(+), 40 deletions(-) diff --git a/sd-card/html/common.js b/sd-card/html/common.js index ba170dee..3c73bb51 100644 --- a/sd-card/html/common.js +++ b/sd-card/html/common.js @@ -1,7 +1,7 @@ /* The UI can also be run locally, but you have to set the IP of your devide accordingly. * And you also might have to disable CORS in your webbrowser! */ -var domainname_for_testing = "192.168.178.62"; +var domainname_for_testing = "192.168.1.153"; diff --git a/sd-card/html/edit_analog.html b/sd-card/html/edit_analog.html index 3c104131..a30bb2a5 100644 --- a/sd-card/html/edit_analog.html +++ b/sd-card/html/edit_analog.html @@ -85,9 +85,6 @@ th, td { Number: @@ -105,8 +102,6 @@ th, td { @@ -120,16 +115,16 @@ th, td { x: Δx: - + y: Δy: - + - + @@ -159,8 +154,8 @@ th, td { cofcat, param, enhanceCon = false; - lockAR = true; - lockSizes = true; + lockAspectRatio = true; + lockSizes = false; domainname = getDomainname(); @@ -222,6 +217,7 @@ function deleteROI(){ aktindex = ROIInfo.length - 1; } UpdateROIs(); + draw(); } function newROI(){ @@ -259,12 +255,13 @@ function moveNext(){ UpdateROIs(); } -function changelockAR(){ - lockAR = document.getElementById("lockAR").checked; +function changelockAspectRatio(){ + lockAspectRatio = document.getElementById("lockAspectRatio").checked; } function changelockSizes(){ lockSizes = document.getElementById("lockSizes").checked; + UpdateROIs(); } function changeCCW(){ @@ -283,7 +280,7 @@ function changeCCW(){ function ChangeSelection(){ aktindex = parseInt(document.getElementById("index").value); -// lockAR = true; +// lockAspectRatio = true; UpdateROIs(); } @@ -317,6 +314,7 @@ function UpdateROIs(_sel){ document.getElementById("newROI").disabled = false; document.getElementById("deleteROI").disabled = true; document.getElementById("index").disabled = true; + document.getElementById("saveroi").disabled = true; document.getElementById("renameROI").disabled = true; document.getElementById("moveNext").disabled = true; document.getElementById("movePrevious").disabled = true; @@ -361,7 +359,7 @@ function UpdateROIs(_sel){ document.getElementById("moveNext").disabled = true; } - document.getElementById("lockAR").checked = lockAR; + document.getElementById("lockAspectRatio").checked = lockAspectRatio; document.getElementById("lockSizes").checked = lockSizes; document.getElementById("refx").value = ROIInfo[aktindex]["x"]; @@ -419,6 +417,29 @@ function UpdateROIs(_sel){ param = getConfigParameters(); cofcat = getConfigCategory(); UpdateNUMBERS(); + + /* Check if the ROIs have same dy and dx. If so, tick the sync checkbox */ + var all_dx_dy_Identical = true; + if (ROIInfo.length > 1) { + for (var i = 1; i < (ROIInfo.length); ++i) { // 2nd .. last ROI + if (parseInt(ROIInfo[i].dx) != parseInt(ROIInfo[0].dx) || + parseInt(ROIInfo[i].dy) != parseInt(ROIInfo[0].dy)) { + all_dx_dy_Identical = false; + break; + } + } + } + + if (all_dx_dy_Identical) { + lockSizes = true; + console.log("All ROI have the same dX and dY, ticking the sync checkbox!"); + document.getElementById("lockSizes").checked = lockSizes; + } + else { + console.log("Not all ROI have the same dX and dY, unticking the sync checkbox!"); + } + + drawImage(); draw(); } @@ -510,11 +531,17 @@ function drawTextBG(context, txt, x, y, padding) { if (typeof ROIInfo === 'undefined') { // During init, ROIInfo is not defined yet return; } + + var canvas = document.getElementById('canvas'); var context = canvas.getContext('2d'); context.drawImage(imageObj, 0, 0); + if (ROIInfo.length == 0) { + return; + } + context.font = "15px Arial"; context.fillStyle = "red"; context.textAlign = "center"; @@ -540,7 +567,7 @@ function drawTextBG(context, txt, x, y, padding) { { if (_nb != _number) { - lw = 1; + lw = 2; context.lineWidth = lw; context.strokeStyle = "#990000"; var x0 = parseInt(ROIInfo[_nb].x) - parseInt(lw/2); @@ -549,12 +576,24 @@ function drawTextBG(context, txt, x, y, padding) { var dy = parseInt(ROIInfo[_nb].dy) + parseInt(lw); context.strokeRect(x0, y0, dx, dy); drawTextBG(context, ROIInfo[_nb]["name"], x0+dx/2-0.5, y0-13, 5); + + lw = 1; + var x0 = parseInt(ROIInfo[_nb].x) - parseInt(lw/2); + var y0 = parseInt(ROIInfo[_nb].y) - parseInt(lw/2); + var dx = parseInt(ROIInfo[_nb].dx) + parseInt(lw); + var dy = parseInt(ROIInfo[_nb].dy) + parseInt(lw); + context.strokeRect(x0, y0, dx, dy); + context.lineWidth = lw; + context.beginPath(); + context.arc(x0+dx/2, y0+dy/2, dx/2, 0, 2 * Math.PI); + context.moveTo(x0+dx/2, y0); + context.lineTo(x0+dx/2, y0+dy); + context.moveTo(x0, y0+dy/2); + context.lineTo(x0+dx, y0+dy/2); + context.stroke(); } - } - - lw = 4 context.lineWidth = lw; context.strokeStyle = "#FF0000"; @@ -622,7 +661,7 @@ function drawTextBG(context, txt, x, y, padding) { zw = getCoords(this) - if (lockAR) { + if (lockAspectRatio) { rect.h = (e.pageY - zw.top) - rect.startY; rect.w = Math.round(rect.h * ROIInfo[aktindex]["ar"]); } else { @@ -657,7 +696,7 @@ function drawTextBG(context, txt, x, y, padding) { if (!drag) { rect.w = document.getElementById("refdx").value; rect.h = document.getElementById("refdy").value; - if (lockAR) { + if (lockAspectRatio) { rect.w = Math.round(rect.h * ROIInfo[aktindex]["ar"]); document.getElementById("refdx").value = rect.w; } @@ -672,7 +711,7 @@ function drawTextBG(context, txt, x, y, padding) { if (!drag) { rect.w = document.getElementById("refdx").value; rect.h = document.getElementById("refdy").value; - if (lockAR) { + if (lockAspectRatio) { rect.h = Math.round(rect.w / ROIInfo[aktindex]["ar"]); document.getElementById("refdy").value = rect.h; } diff --git a/sd-card/html/edit_config_param.html b/sd-card/html/edit_config_param.html index 0853b0b6..625c8d7f 100644 --- a/sd-card/html/edit_config_param.html +++ b/sd-card/html/edit_config_param.html @@ -1309,6 +1309,7 @@ textarea { +

Use timezones.html to find your correct settings.

$TOOLTIP_System_TimeZone diff --git a/sd-card/html/edit_digits.html b/sd-card/html/edit_digits.html index b8e8fce8..12b70356 100644 --- a/sd-card/html/edit_digits.html +++ b/sd-card/html/edit_digits.html @@ -81,9 +81,6 @@ th, td { Number: @@ -101,8 +98,6 @@ th, td { @@ -115,7 +110,7 @@ th, td { x: Δx: - + y: @@ -154,8 +149,8 @@ th, td { cofcat, param, enhanceCon = false; - lockAR = true; - lockSizes = true; + lockAspectRatio = true; + lockSizes = false; lockSpaceEquidistant = true; space = 3; domainname = getDomainname(); @@ -269,12 +264,14 @@ function moveNext(){ valuemanualchanged(); } -function changelockAR(){ - lockAR = document.getElementById("lockAR").checked; +function changelockAspectRatio(){ + lockAspectRatio = document.getElementById("lockAspectRatio").checked; } function changelockSizes(){ lockSizes = document.getElementById("lockSizes").checked; + UpdateROIs(); + valuemanualchangedspace(); if (!lockSizes) { firework.launch("For best results it is in most cases advised to keep the y, Δx and Δy identical!", 'warning', 10000); @@ -283,11 +280,18 @@ function changelockSizes(){ function changeLockSpaceEquidistant(){ lockSpaceEquidistant = document.getElementById("lockSpaceEquidistant").checked; + if (!lockSpaceEquidistant) { + document.getElementById("space").disabled = true; + } + else { + document.getElementById("space").disabled = false; + } + UpdateROIs(); } function ChangeSelection(){ aktindex = parseInt(document.getElementById("index").value); -// lockAR = true; +// lockAspectRatio = true; UpdateROIs(); } @@ -367,10 +371,16 @@ function UpdateROIs(_sel){ document.getElementById("moveNext").disabled = true; } - document.getElementById("lockAR").checked = lockAR; + document.getElementById("lockAspectRatio").checked = lockAspectRatio; document.getElementById("lockSizes").checked = lockSizes; document.getElementById("lockSpaceEquidistant").checked = lockSpaceEquidistant; document.getElementById("space").value = space; + if (!lockSpaceEquidistant) { + document.getElementById("space").disabled = true; + } + else { + document.getElementById("space").disabled = false; + } document.getElementById("refx").value = ROIInfo[aktindex]["x"]; document.getElementById("refy").value = ROIInfo[aktindex]["y"]; @@ -423,8 +433,53 @@ function UpdateROIs(_sel){ cofcat = getConfigCategory(); UpdateNUMBERS(); - space = ROIInfo[1].x - parseInt(ROIInfo[0].x) - parseInt(ROIInfo[0].dx); - document.getElementById("space").value = space; + + /* Check if the ROIs are equidistant. Only if not, untick the checkbox */ + if (ROIInfo.length > 1) { + var distanceROI0_to_ROI1 = parseInt(ROIInfo[1].x) - (parseInt(ROIInfo[0].x) + parseInt(ROIInfo[0].dx)); // Distance between 1st and 2nd ROI + //console.log("0->1: " + distanceROI0_to_ROI1); + for (var i = 1; i < (ROIInfo.length - 1); ++i) { // 2nd .. 2nd-last ROI + //console.log(i + "->" + i+1 + ": " + (parseInt(ROIInfo[i+1].x) - (parseInt(ROIInfo[i].x) + parseInt(ROIInfo[i].dx)))); + if (distanceROI0_to_ROI1 != (parseInt(ROIInfo[i+1].x) - (parseInt(ROIInfo[i].x) + parseInt(ROIInfo[i].dx)))) { + console.log("Not equidistant, unticking the checkbox!"); + lockSpaceEquidistant = false; + document.getElementById("lockSpaceEquidistant").checked = lockSpaceEquidistant; + if (!lockSpaceEquidistant) { + document.getElementById("space").disabled = true; + } + else { + document.getElementById("space").disabled = false; + } + break; + } + } + } + /* Check if the ROIs have same y, dy and dx. If so, tick the sync checkbox */ + var all_y_dx_dy_Identical = true; + if (ROIInfo.length > 1) { + for (var i = 1; i < (ROIInfo.length); ++i) { // 2nd .. last ROI + if (parseInt(ROIInfo[i].y) != parseInt(ROIInfo[0].y) || + parseInt(ROIInfo[i].dx) != parseInt(ROIInfo[0].dx) || + parseInt(ROIInfo[i].dy) != parseInt(ROIInfo[0].dy)) { + all_y_dx_dy_Identical = false; + break; + } + } + } + + if (all_y_dx_dy_Identical) { + lockSizes = true; + console.log("All ROI have the same Y, dX and dY, ticking the sync checkbox!"); + document.getElementById("lockSizes").checked = lockSizes; + } + else { + console.log("Not all ROI have the same Y, dX and dY, unticking the sync checkbox!"); + } + + if (ROIInfo.length > 1) { + space = ROIInfo[1].x - parseInt(ROIInfo[0].x) - parseInt(ROIInfo[0].dx); + document.getElementById("space").value = space; + } drawImage(); draw(); @@ -558,8 +613,19 @@ function draw() { var dy = parseInt(ROIInfo[_nb].dy) + parseInt(lw); context.strokeRect(x0, y0, dx, dy); drawTextBG(context, ROIInfo[_nb]["name"], x0+dx/2, y0-12, 5); - } + lw = 1 + var x0 = parseInt(ROIInfo[_nb].x) - parseInt(lw/2); + var y0 = parseInt(ROIInfo[_nb].y) - parseInt(lw/2); + var dx = parseInt(ROIInfo[_nb].dx) + parseInt(lw); + var dy = parseInt(ROIInfo[_nb].dy) + parseInt(lw); + context.lineWidth = lw; + context.beginPath(); + context.moveTo(x0, y0+dy/2); + context.lineTo(x0+dx, y0+dy/2); + context.stroke(); + context.strokeRect(x0+dx*0.2, y0+dy*0.2, dx*0.6, dy*0.6); + } } lw = 4 @@ -635,7 +701,7 @@ function draw() { return; } - if (lockAR) { + if (lockAspectRatio) { rect.h = (e.pageY - zw.top) - rect.startY; rect.w = Math.round(rect.h * ROIInfo[aktindex]["ar"]); } else { @@ -675,7 +741,7 @@ function draw() { rect.w = document.getElementById("refdx").value; rect.h = document.getElementById("refdy").value; - if (lockAR) { + if (lockAspectRatio) { rect.w = Math.round(rect.h * ROIInfo[aktindex]["ar"]); document.getElementById("refdx").value = rect.w; } @@ -700,7 +766,7 @@ function draw() { rect.w = document.getElementById("refdx").value; rect.h = document.getElementById("refdy").value; - if (lockAR) { + if (lockAspectRatio) { rect.h = Math.round(rect.w / ROIInfo[aktindex]["ar"]); document.getElementById("refdy").value = rect.h; } From 4dd41c486f8828777e0cc7bcdabd29210443b5ca Mon Sep 17 00:00:00 2001 From: CaCO3 Date: Sun, 12 Mar 2023 21:30:23 +0100 Subject: [PATCH 04/87] restart timeout on progress, catch error (#2170) * restart timeout on progress, catch error * . --------- Co-authored-by: CaCO3 --- sd-card/html/backup.html | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/sd-card/html/backup.html b/sd-card/html/backup.html index 260354d9..c55c3d3d 100644 --- a/sd-card/html/backup.html +++ b/sd-card/html/backup.html @@ -122,13 +122,13 @@ function fetchFiles(urls, filesData, index, retry, zipFilename) { xhr.timeout = 5000; // time in milliseconds } else if (retry == 2) { // longer timeout - xhr.timeout = 20000; // time in milliseconds + xhr.timeout = 10000; // time in milliseconds } else if (retry == 3) { // longer timeout - xhr.timeout = 30000; // time in milliseconds + xhr.timeout = 20000; // time in milliseconds } else { // very long timeout - xhr.timeout = 60000; // time in milliseconds + xhr.timeout = 30000; // time in milliseconds } xhr.onload = () => { // Request finished @@ -146,6 +146,20 @@ function fetchFiles(urls, filesData, index, retry, zipFilename) { } }; + xhr.onprogress = (e) => { // XMLHttpRequest progress ... extend timeout + xhr.timeout = xhr.timeout + 500; + }; + + xhr.onerror = (e) => { // XMLHttpRequest error loading + console.log("Error on fetching " + url + "!"); + if (retry > 5) { + setStatus("Backup failed, please restart the device and try again!"); + } + else { + fetchFiles(urls, filesData, index, retry+1, zipFilename); + } + }; + xhr.ontimeout = (e) => { // XMLHttpRequest timed out console.log("Timeout on fetching " + url + "!"); if (retry > 5) { From dfc45772b772cc1ce549d2d784454c990113e2b6 Mon Sep 17 00:00:00 2001 From: jomjol <30766535+jomjol@users.noreply.github.com> Date: Sun, 12 Mar 2023 22:19:30 +0100 Subject: [PATCH 05/87] BugFix #2167 --- code/main/softAP.cpp | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/code/main/softAP.cpp b/code/main/softAP.cpp index 62eec287..191bccf4 100644 --- a/code/main/softAP.cpp +++ b/code/main/softAP.cpp @@ -212,55 +212,55 @@ esp_err_t config_ini_handler(httpd_req_t *req) { ESP_LOGD(TAG, "Query: %s", _query); - if (httpd_query_key_value(_query, "ssid", _valuechar, 30) == ESP_OK) + if (httpd_query_key_value(_query, "ssid", _valuechar, 100) == ESP_OK) { ESP_LOGD(TAG, "ssid is found: %s", _valuechar); ssid = UrlDecode(std::string(_valuechar)); } - if (httpd_query_key_value(_query, "pwd", _valuechar, 30) == ESP_OK) + if (httpd_query_key_value(_query, "pwd", _valuechar, 100) == ESP_OK) { ESP_LOGD(TAG, "pwd is found: %s", _valuechar); pwd = UrlDecode(std::string(_valuechar)); } - if (httpd_query_key_value(_query, "ssid", _valuechar, 30) == ESP_OK) + if (httpd_query_key_value(_query, "ssid", _valuechar, 100) == ESP_OK) { ESP_LOGD(TAG, "ssid is found: %s", _valuechar); ssid = UrlDecode(std::string(_valuechar)); } - if (httpd_query_key_value(_query, "hn", _valuechar, 30) == ESP_OK) + if (httpd_query_key_value(_query, "hn", _valuechar, 100) == ESP_OK) { ESP_LOGD(TAG, "hostname is found: %s", _valuechar); hn = UrlDecode(std::string(_valuechar)); } - if (httpd_query_key_value(_query, "ip", _valuechar, 30) == ESP_OK) + if (httpd_query_key_value(_query, "ip", _valuechar, 100) == ESP_OK) { ESP_LOGD(TAG, "ip is found: %s", _valuechar); ip = UrlDecode(std::string(_valuechar)); } - if (httpd_query_key_value(_query, "gw", _valuechar, 30) == ESP_OK) + if (httpd_query_key_value(_query, "gw", _valuechar, 100) == ESP_OK) { ESP_LOGD(TAG, "gateway is found: %s", _valuechar); gw = UrlDecode(std::string(_valuechar)); } - if (httpd_query_key_value(_query, "nm", _valuechar, 30) == ESP_OK) + if (httpd_query_key_value(_query, "nm", _valuechar, 100) == ESP_OK) { ESP_LOGD(TAG, "netmask is found: %s", _valuechar); nm = UrlDecode(std::string(_valuechar)); } - if (httpd_query_key_value(_query, "dns", _valuechar, 30) == ESP_OK) + if (httpd_query_key_value(_query, "dns", _valuechar, 100) == ESP_OK) { ESP_LOGD(TAG, "dns is found: %s", _valuechar); dns = UrlDecode(std::string(_valuechar)); } - if (httpd_query_key_value(_query, "rssithreshold", _valuechar, 30) == ESP_OK) + if (httpd_query_key_value(_query, "rssithreshold", _valuechar, 100) == ESP_OK) { ESP_LOGD(TAG, "rssithreshold is found: %s", _valuechar); rssithreshold = UrlDecode(std::string(_valuechar)); From eefc41d6ff5d9407a0959ae508418ab43c870bb0 Mon Sep 17 00:00:00 2001 From: CaCO3 Date: Sun, 12 Mar 2023 22:46:16 +0100 Subject: [PATCH 06/87] Release 15.1 preparations (#2171) * Update Changelog.md * Update Changelog.md * Update Changelog.md * Update changelog * Fix links to PR * Formating * Update Changelog.md * Update Changelog.md * Update Changelog.md * Update Changelog.md * Update Changelog.md * Update Changelog.md * Update Changelog.md * Update Changelog.md * Update Changelog.md * Update Changelog.md * Update Changelog.md * Update Changelog.md --------- Co-authored-by: Slider0007 Co-authored-by: Slider0007 <115730895+Slider0007@users.noreply.github.com> --- Changelog.md | 67 ++++++++++++++++++++++++++++++++-------------------- 1 file changed, 42 insertions(+), 25 deletions(-) diff --git a/Changelog.md b/Changelog.md index 1fed5b1e..670ae606 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,47 +1,63 @@ -## [Unreleased] - -xxx +## [15.1.0] - 2023-03-12 ### Update Procedure Update Procedure see [online documentation](https://jomjol.github.io/AI-on-the-edge-device-docs/Installation/#update-ota-over-the-air) -:bangbang: Afterwards you should force-reload the Web Interface (usually Ctrl-F5 will do it). +:bangbang: Afterwards you should force-reload the Web Interface (usually Ctrl-F5 will do it)! + +:bangbang: Afterwards you should check your configuration for errors! ### Changes -This release only migrates some parameters, see #2023 for details and a list of all parameter changes. -The parameter migration happens automatically on the next startup. No user interaction is required. -A backup of the config is stored on the SD-card as `config.bak`. - -Beside of the parameter change and the bugfix listed below, no changes are contained in this release! - -If you want to revert back to `v14` or earlier, you will have to revert the migration changes in `config.ini` manually! +For a full list of changes see [Full list of changes](https://github.com/jomjol/AI-on-the-edge-device/compare/v15.0.3...v15.1.0) #### Added - -- Additional interface to InfluxDB Version 2 upwards -- Updated the Hybrid CNN network to `dig-cont_0610_s3` -- :bangbang: Update Camera driver: contrast, brightness and saturation now working - - :bangbang: **Attention**: this can effect old version as well, because there not all settings were effective! +- The Configuration page has now tooltips with enhanced documentation +- MQTT: + - Added `GJ` (`gigajoule`) as an energy meter unit + - Removed State Class and unit from `raw` topic + - Various Improvements (Only send Homeassistant Discovery the first time we connect, ...) (https://github.com/jomjol/AI-on-the-edge-device/pull/2091 +- Added Expert Parameter to change CPU Clock from `160` to `240 Mhz` +- SD card basic read/write check and a folder/file presence check at boot to indicate SD card issues or missing folders / files ([#2085](https://github.com/jomjol/AI-on-the-edge-device/pull/2085)) +- Simplified "WIFI roaming" by client triggered channel scan (AP switching at low RSSI) -> using expert parameter "RSSIThreshold" ([#2120](https://github.com/jomjol/AI-on-the-edge-device/pull/2120)) +- Log WLAN disconnect reason codes (see [WLAN disconnect reasons](https://jomjol.github.io/AI-on-the-edge-device-docs/WLAN-disconnect-reason)) +- Support of InfluxDB v2 ([#2004](https://github.com/jomjol/AI-on-the-edge-device/pull/2004)) #### Changed +- Updated models (tflite files), removed old versions (https://github.com/jomjol/AI-on-the-edge-device/pull/2089, https://github.com/jomjol/AI-on-the-edge-device/pull/2133) + :bangbang: **Attention:** Update your configuration! +- Various Web interface Improvements/Enhancements: + - Restructured Menu (Needs cache clearing to be applied) + - Enhanced `Previous Value` page + - Improved/faster Graph page + - Various minor improvements + - ROI config pages improvements + - Improved Backup Functionality +- Added log file logs for Firmware Update +- Improved memory management (moved various stuff to external PSRAM, https://github.com/jomjol/AI-on-the-edge-device/pull/2117) +- Camera driver update: Support of contrast and saturation ([#2048](https://github.com/jomjol/AI-on-the-edge-device/pull/2048)) + :bangbang: **Attention**: This could have impact to old configurations. Please check your configuration and potentially adapt parametrization, if detection is negativly affected. +- Improved error handling and provide more verbose output in error cases during boot phase ([#2020](https://github.com/jomjol/AI-on-the-edge-device/pull/2020)) +- Red board LED is indicating more different errors and states (see [Status LED Blink Codes](https://jomjol.github.io/AI-on-the-edge-device-docs/StatusLED-BlinkCodes)) +- Logfile: Print start indication block after time is synced to indicate start in logfile after a cold boot +- `Image Quality Index`: Limit lower input range to 8 to avoid system instabilities -- [#2023](https://github.com/jomjol/AI-on-the-edge-device/pull/2023) Migrated Parameters -- Removed old `Topic` parameter, it is not used anymore #### Fixed -- n.a. +- Various minor fixes +- Added State Class "measurement" to rate_per_time_unit +- GPIO: Avoid MQTT publishing to empty topic when "MQTT enable" flag is not set +- Fix timezone config parser +- Remote Setup truncated long passwords (https://github.com/jomjol/AI-on-the-edge-device/issues/2167) #### Removed - - n.a. ## [15.0.3] - 2023-02-28 -**Parameter Migration** +**Name: Parameter Migration** ### Update Procedure @@ -81,7 +97,7 @@ If you want to revert back to `v14` or earlier, you will have to revert the migr ## [14.0.3] -2023-02-05 -**Stabilization and Improved User Experience** +**Name: Stabilization and Improved User Experience** Thanks to over 80 Pull Requests from 6 contributors, we can anounce another great release with many many improvements and new features: @@ -135,7 +151,7 @@ For a full list of changes see [Full list of changes](https://github.com/jomjol/ ## [13.0.8] - 2022-12-19 -**Home Assistant MQTT Discovery Support** +**Name: Home Assistant MQTT Discovery Support** ### Update Procedure see [online documentation](https://jomjol.github.io/AI-on-the-edge-device-docs/Installation/#update-ota-over-the-air) @@ -215,7 +231,7 @@ For a full list of changes see [Full list of changes](https://github.com/jomjol/ ## [12.0.1] 2022-09-29 -Improve **u**ser e**x**perience +Name: Improve **u**ser e**x**perience :bangbang: The release breaks a few things in ota update :bangbang: @@ -883,6 +899,7 @@ External Illumination - Initial Version +[15.1.0]: https://github.com/jomjol/AI-on-the-edge-device/compare/v15.0.3...v15.1.0 [15.0.3]: https://github.com/jomjol/AI-on-the-edge-device/compare/v14.0.3...v15.0.3 [14.0.3]: https://github.com/jomjol/AI-on-the-edge-device/compare/v13.0.8...v14.0.3 [13.0.8]: https://github.com/jomjol/AI-on-the-edge-device/compare/v12.0.1...v13.0.8 From e81a7eebe816371e72bd2789a87e03f6d6927a6b Mon Sep 17 00:00:00 2001 From: CaCO3 Date: Mon, 13 Mar 2023 21:32:39 +0100 Subject: [PATCH 07/87] fix typo --- sd-card/html/edit_config_param.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sd-card/html/edit_config_param.html b/sd-card/html/edit_config_param.html index 625c8d7f..2c7c883c 100644 --- a/sd-card/html/edit_config_param.html +++ b/sd-card/html/edit_config_param.html @@ -1226,7 +1226,7 @@ textarea { - Automatically Round Start + Automatic Round Start - - - - - - $TOOLTIP_InfluxDB_Measurement - @@ -718,6 +708,34 @@ textarea { $TOOLTIP_InfluxDB_password + + +
+ InfluxDB Individual Parameters: + + + + + + + + + + + + $TOOLTIP_InfluxDB_NUMBER.Measurement + + + + + + + + + + $TOOLTIP_InfluxDB_NUMBER.Field + @@ -749,16 +767,6 @@ textarea { $TOOLTIP_InfluxDBv2_Database - - - - - - - - - $TOOLTIP_InfluxDBv2_Measurement - @@ -782,20 +790,30 @@ textarea {
- Postprocessing Individual Parameters: - - - + + - + - $TOOLTIP_InfluxDBv2_NUMBER.Fieldname + $TOOLTIP_InfluxDBv2_NUMBER.Measurement + + + + + + + + + + $TOOLTIP_InfluxDBv2_NUMBER.Field @@ -1423,11 +1441,16 @@ function InitIndivParameter() var _index = document.getElementById("Numbers_value1"); while (_index.length) _index.remove(0); - var _indexInflux = document.getElementById("NumbersInfluxDB_value1"); + + var _indexInflux = document.getElementById("NumbersInfluxDBv2_value1"); while (_indexInflux.length) _indexInflux.remove(0); - for (var i = 0; i < NUMBERS.length; ++i){ + var _indexInfluxv1 = document.getElementById("NumbersInfluxDB_value1"); + while (_indexInflux.length) + _indexInfluxv1.remove(0); + + for (var i = 0; i < NUMBERS.length; ++i){ var option = document.createElement("option"); option.text = NUMBERS[i]["name"]; option.value = i; @@ -1437,9 +1460,16 @@ function InitIndivParameter() optionInflux.text = NUMBERS[i]["name"]; optionInflux.value = i; _indexInflux.add(optionInflux); - } + + var optionInfluxv1 = document.createElement("option"); + optionInfluxv1.text = NUMBERS[i]["name"]; + optionInfluxv1.value = i; + _indexInfluxv1.add(optionInfluxv1); + + } _index.selectedIndex = 0; _indexInflux.selectedIndex = 0; + _indexInfluxv1.selectedIndex = 0; } @@ -1741,7 +1771,10 @@ function UpdateInputIndividual(sel) ReadParameter(param, "PostProcessing", "ExtendedResolution", false, NUNBERSAkt) ReadParameter(param, "PostProcessing", "IgnoreLeadingNaN", false, NUNBERSAkt) ReadParameter(param, "PostProcessing", "AllowNegativeRates", false, NUNBERSAkt) - ReadParameter(param, "InfluxDBv2", "Fieldname", true, NUNBERSAkt) + ReadParameter(param, "InfluxDB", "Field", true, NUNBERSAkt) + ReadParameter(param, "InfluxDBv2", "Field", true, NUNBERSAkt) + ReadParameter(param, "InfluxDB", "Measurement", true, NUNBERSAkt) + ReadParameter(param, "InfluxDBv2", "Measurement", true, NUNBERSAkt) } // var sel = document.getElementById("Numbers_value1"); @@ -1753,7 +1786,10 @@ function UpdateInputIndividual(sel) WriteParameter(param, category, "PostProcessing", "ExtendedResolution", false, NUNBERSAkt); WriteParameter(param, category, "PostProcessing", "IgnoreLeadingNaN", false, NUNBERSAkt); WriteParameter(param, category, "PostProcessing", "AllowNegativeRates", false, NUNBERSAkt); - WriteParameter(param, category, "InfluxDBv2", "Fieldname", true, NUNBERSAkt); + WriteParameter(param, category, "InfluxDB", "Field", true, NUNBERSAkt); + WriteParameter(param, category, "InfluxDBv2", "Field", true, NUNBERSAkt); + WriteParameter(param, category, "InfluxDB", "Measurement", true, NUNBERSAkt); + WriteParameter(param, category, "InfluxDBv2", "Measurement", true, NUNBERSAkt); } function UpdateInput() { @@ -1805,16 +1841,17 @@ function UpdateInput() { WriteParameter(param, category, "InfluxDB", "Uri", true); WriteParameter(param, category, "InfluxDB", "Database", true); - WriteParameter(param, category, "InfluxDB", "Measurement", true); +// WriteParameter(param, category, "InfluxDB", "Measurement", true); WriteParameter(param, category, "InfluxDB", "user", true); WriteParameter(param, category, "InfluxDB", "password", true); +// WriteParameter(param, category, "InfluxDB", "Field", true); WriteParameter(param, category, "InfluxDBv2", "Uri", true); WriteParameter(param, category, "InfluxDBv2", "Database", true); - WriteParameter(param, category, "InfluxDBv2", "Measurement", true); +// WriteParameter(param, category, "InfluxDBv2", "Measurement", true); WriteParameter(param, category, "InfluxDBv2", "Org", true); WriteParameter(param, category, "InfluxDBv2", "Token", true); - WriteParameter(param, category, "InfluxDBv2", "Fieldname", true); +// WriteParameter(param, category, "InfluxDBv2", "Field", true); WriteParameter(param, category, "GPIO", "IO0", true); WriteParameter(param, category, "GPIO", "IO1", true); @@ -1945,7 +1982,7 @@ function ReadParameterAll() ReadParameter(param, "InfluxDBv2", "Measurement", true); ReadParameter(param, "InfluxDBv2", "Org", true); ReadParameter(param, "InfluxDBv2", "Token", true); -// ReadParameter(param, "InfluxDB", "Fieldname", true); +// ReadParameter(param, "InfluxDB", "Field", true); ReadParameter(param, "GPIO", "IO0", true); ReadParameter(param, "GPIO", "IO1", true); @@ -2086,11 +2123,26 @@ function numberChanged() _neu = sel.selectedIndex; UpdateInputIndividual(sel); - var _selInflux = document.getElementById("NumbersInfluxDB_value1"); + var _selInflux = document.getElementById("NumbersInfluxDBv2_value1"); if (_selInflux.selectedIndex != _neu) _selInflux.selectedIndex = _neu } +function numberInfluxDBv2Changed() +{ + var sel = document.getElementById("NumbersInfluxDBv2_value1"); + _neu = sel.selectedIndex; + UpdateInputIndividual(sel); + + var _sel2 = document.getElementById("Numbers_value1"); + if (_sel2.selectedIndex != _neu) + _sel2.selectedIndex = _neu + + var _sel3 = document.getElementById("NumbersInfluxDB_value1"); + if (_sel3.selectedIndex != _neu) + _sel3.selectedIndex = _neu +} + function numberInfluxDBChanged() { var sel = document.getElementById("NumbersInfluxDB_value1"); @@ -2099,7 +2151,11 @@ function numberInfluxDBChanged() var _sel2 = document.getElementById("Numbers_value1"); if (_sel2.selectedIndex != _neu) - _sel2.selectedIndex = _neu + _sel2.selectedIndex = _neu + + var _sel3 = document.getElementById("NumbersInfluxDBv2_value1"); + if (_sel3.selectedIndex != _neu) + _sel3.selectedIndex = _neu } LoadConfigNeu(); diff --git a/sd-card/html/readconfigparam.js b/sd-card/html/readconfigparam.js index 5fa7ed0f..a2138143 100644 --- a/sd-card/html/readconfigparam.js +++ b/sd-card/html/readconfigparam.js @@ -191,9 +191,11 @@ function ParseConfig() { param[catname] = new Object(); ParamAddValue(param, catname, "Uri"); ParamAddValue(param, catname, "Database"); - ParamAddValue(param, catname, "Measurement"); +// ParamAddValue(param, catname, "Measurement"); ParamAddValue(param, catname, "user"); ParamAddValue(param, catname, "password"); + ParamAddValue(param, catname, "Measurement", 1, true); + ParamAddValue(param, catname, "Field", 1, true); var catname = "InfluxDBv2"; category[catname] = new Object(); @@ -202,10 +204,11 @@ function ParseConfig() { param[catname] = new Object(); ParamAddValue(param, catname, "Uri"); ParamAddValue(param, catname, "Database"); - ParamAddValue(param, catname, "Measurement"); +// ParamAddValue(param, catname, "Measurement"); ParamAddValue(param, catname, "Org"); ParamAddValue(param, catname, "Token"); - ParamAddValue(param, catname, "Fieldname", 1, true); + ParamAddValue(param, catname, "Measurement", 1, true); + ParamAddValue(param, catname, "Field", 1, true); var catname = "GPIO"; category[catname] = new Object(); From eebdd7c6eb21fd8f459e99bea29252d2bceab784 Mon Sep 17 00:00:00 2001 From: CaCO3 Date: Sun, 23 Apr 2023 16:37:12 +0200 Subject: [PATCH 38/87] Update Changelog.md (#2344) * Update Changelog.md * Update Changelog.md --- Changelog.md | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/Changelog.md b/Changelog.md index bbc4a41f..1b63fa03 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,3 +1,36 @@ +## [15.2.0] - 2023-04-23 + +### Update Procedure + +Update Procedure see [online documentation](https://jomjol.github.io/AI-on-the-edge-device-docs/Installation/#update-ota-over-the-air) + +### Changes + +For a full list of changes see [Full list of changes](https://github.com/jomjol/AI-on-the-edge-device/compare/v15.1.1...v15.2.0) + +#### Added + +- [#2286](https://github.com/jomjol/AI-on-the-edge-device/pull/2286) Implement a camera livestream handler +- [#2252](https://github.com/jomjol/AI-on-the-edge-device/pull/2252) Set prevalue using MQTT + set prevalue to RAW value (REST+MQTT) + +#### Changed + +- [#2285](https://github.com/jomjol/AI-on-the-edge-device/pull/2285) Re-implemented PSRAM usage +- [#2325](https://github.com/jomjol/AI-on-the-edge-device/pull/2325) Keep MainFlowTask alive to handle reboot +- [#2305](https://github.com/jomjol/AI-on-the-edge-device/pull/2305) Migration of PlatformIO `5.2.0` to `6.1.0` (resp. ESP IDF from `4.4.2` to `5.0.1`) +- Various cleanup and refactoring + +#### Fixed + +- [#2326](https://github.com/jomjol/AI-on-the-edge-device/pull/2326) Activate save button after Analogue ROI creationSet prevalue using MQTT + set prevalue to RAW value (REST+MQTT) +- [#2283](https://github.com/jomjol/AI-on-the-edge-device/pull/2283) Fix Timezone issues on InfluxDB +- Various minor fixes + +#### Removed + +- n.a. + + ## [15.1.1] - 2023-03-23 ### Update Procedure @@ -933,6 +966,7 @@ External Illumination - Initial Version +[15.2.0]: https://github.com/jomjol/AI-on-the-edge-device/compare/v15.1.1...v15.1.2 [15.1.1]: https://github.com/jomjol/AI-on-the-edge-device/compare/v15.1.0...v15.1.1 [15.1.0]: https://github.com/jomjol/AI-on-the-edge-device/compare/v15.0.3...v15.1.0 [15.0.3]: https://github.com/jomjol/AI-on-the-edge-device/compare/v14.0.3...v15.0.3 From 929796c87f418ad82d4b8c75de825b72d09c43c1 Mon Sep 17 00:00:00 2001 From: CaCO3 Date: Sun, 23 Apr 2023 16:45:20 +0200 Subject: [PATCH 39/87] merge conflicts --- .../jomjol_flowcontroll/ClassFlowAlignment.cpp | 4 ---- code/components/jomjol_helper/psram.cpp | 18 ------------------ code/components/jomjol_helper/psram.h | 10 ---------- .../jomjol_image_proc/CImageBasis.cpp | 10 ---------- code/components/jomjol_mqtt/interface_mqtt.cpp | 17 ----------------- .../jomjol_tfliteclass/CTfLiteClass.cpp | 15 --------------- code/sdkconfig.defaults | 5 ----- 7 files changed, 79 deletions(-) diff --git a/code/components/jomjol_flowcontroll/ClassFlowAlignment.cpp b/code/components/jomjol_flowcontroll/ClassFlowAlignment.cpp index 8d6f7600..f7ff40d2 100644 --- a/code/components/jomjol_flowcontroll/ClassFlowAlignment.cpp +++ b/code/components/jomjol_flowcontroll/ClassFlowAlignment.cpp @@ -190,11 +190,7 @@ bool ClassFlowAlignment::doFlow(string time) if (!ImageTMP) { -<<<<<<< HEAD ImageTMP = new CImageBasis("tmpImage", ImageBasis); // Make sure the name does not get change, it is relevant for the PSRAM allocation! -======= - ImageTMP = new CImageBasis("ImageTMP", ImageBasis); ->>>>>>> master if (!ImageTMP) { LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Can't allocate tmpImage -> Exec this round aborted!"); diff --git a/code/components/jomjol_helper/psram.cpp b/code/components/jomjol_helper/psram.cpp index d762f241..8c7dcabb 100644 --- a/code/components/jomjol_helper/psram.cpp +++ b/code/components/jomjol_helper/psram.cpp @@ -1,17 +1,12 @@ #include "ClassLogFile.h" -<<<<<<< HEAD #include "../../include/defines.h" #include "psram.h" -======= -#include "esp_heap_caps.h" ->>>>>>> master static const char* TAG = "PSRAM"; using namespace std; -<<<<<<< HEAD void *shared_region = NULL; uint32_t allocatedBytesForSTBI = 0; @@ -128,8 +123,6 @@ void psram_free_shared_tensor_arena_and_model_memory(void) { /******************************************************************* * General *******************************************************************/ -======= ->>>>>>> master void *malloc_psram_heap(std::string name, size_t size, uint32_t caps) { void *ptr; @@ -145,7 +138,6 @@ void *malloc_psram_heap(std::string name, size_t size, uint32_t caps) { } -<<<<<<< HEAD void *realloc_psram_heap(std::string name, void *ptr, size_t size, uint32_t caps) { ptr = heap_caps_realloc(ptr, size, caps); if (ptr != NULL) { @@ -159,18 +151,12 @@ void *realloc_psram_heap(std::string name, void *ptr, size_t size, uint32_t caps } -======= ->>>>>>> master void *calloc_psram_heap(std::string name, size_t n, size_t size, uint32_t caps) { void *ptr; ptr = heap_caps_calloc(n, size, caps); if (ptr != NULL) { -<<<<<<< HEAD LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Allocated " + to_string(size) + " bytes in PSRAM for '" + name + "'"); -======= - LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Allocated " + to_string(size) + " bytes in PSRAM for '" + name + "'"); ->>>>>>> master } else { LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Failed to allocate " + to_string(size) + " bytes in PSRAM for '" + name + "'!"); @@ -183,8 +169,4 @@ void *calloc_psram_heap(std::string name, size_t n, size_t size, uint32_t caps) void free_psram_heap(std::string name, void *ptr) { LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Freeing memory in PSRAM used for '" + name + "'..."); heap_caps_free(ptr); -<<<<<<< HEAD } -======= -} ->>>>>>> master diff --git a/code/components/jomjol_helper/psram.h b/code/components/jomjol_helper/psram.h index cf95f202..4c85bf26 100644 --- a/code/components/jomjol_helper/psram.h +++ b/code/components/jomjol_helper/psram.h @@ -1,4 +1,3 @@ -<<<<<<< HEAD #pragma once #ifndef PSRAM_h #define PSRAM_h @@ -33,12 +32,3 @@ void *calloc_psram_heap(std::string name, size_t n, size_t size, uint32_t caps); void free_psram_heap(std::string name, void *ptr); #endif // PSRAM_h -======= - -#include "esp_heap_caps.h" - -void *malloc_psram_heap(std::string name, size_t size, uint32_t caps); -void *calloc_psram_heap(std::string name, size_t n, size_t size, uint32_t caps); - -void free_psram_heap(std::string name, void *ptr); ->>>>>>> master diff --git a/code/components/jomjol_image_proc/CImageBasis.cpp b/code/components/jomjol_image_proc/CImageBasis.cpp index dc080224..a61ca7cd 100644 --- a/code/components/jomjol_image_proc/CImageBasis.cpp +++ b/code/components/jomjol_image_proc/CImageBasis.cpp @@ -477,7 +477,6 @@ CImageBasis::CImageBasis(string _name, CImageBasis *_copyfrom) memsize = width * height * channels; -<<<<<<< HEAD if (name == "tmpImage") { rgb_image = (unsigned char*)psram_reserve_shared_tmp_image_memory(); @@ -485,9 +484,6 @@ CImageBasis::CImageBasis(string _name, CImageBasis *_copyfrom) else { rgb_image = (unsigned char*)malloc_psram_heap(std::string(TAG) + "->CImageBasis (" + name + ")", memsize, MALLOC_CAP_SPIRAM); } -======= - rgb_image = (unsigned char*)malloc_psram_heap(std::string(TAG) + "->CImageBasis (" + name + ")", memsize, MALLOC_CAP_SPIRAM); ->>>>>>> master if (rgb_image == NULL) { @@ -628,7 +624,6 @@ CImageBasis::~CImageBasis() { RGBImageLock(); -<<<<<<< HEAD if (!externalImage) { if (name == "tmpImage") { // This image should be placed in the shared part of PSRAM @@ -643,11 +638,6 @@ CImageBasis::~CImageBasis() free_psram_heap(std::string(TAG) + "->CImageBasis (" + name + ", " + to_string(memsize) + ")", rgb_image); } } -======= - if (!externalImage) { - //stbi_image_free(rgb_image); - free_psram_heap(std::string(TAG) + "->CImageBasis (" + name + ", " + to_string(memsize) + ")", rgb_image); ->>>>>>> master } RGBImageRelease(); diff --git a/code/components/jomjol_mqtt/interface_mqtt.cpp b/code/components/jomjol_mqtt/interface_mqtt.cpp index 0a2267a8..1ab8f03f 100644 --- a/code/components/jomjol_mqtt/interface_mqtt.cpp +++ b/code/components/jomjol_mqtt/interface_mqtt.cpp @@ -248,7 +248,6 @@ int MQTT_Init() { LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Init"); MQTTdestroy_client(false); -<<<<<<< HEAD esp_mqtt_client_config_t mqtt_cfg = { }; mqtt_cfg.broker.address.uri = uri.c_str(); @@ -263,22 +262,6 @@ int MQTT_Init() { mqtt_cfg.session.last_will.msg_len = (int)(lwt_disconnected.length()); mqtt_cfg.session.keepalive = keepalive; mqtt_cfg.buffer.size = 1536; // size of MQTT send/receive buffer (Default: 1024) -======= - esp_mqtt_client_config_t mqtt_cfg = { - .uri = uri.c_str(), - .client_id = client_id.c_str(), - .lwt_topic = lwt_topic.c_str(), - .lwt_msg = lwt_disconnected.c_str(), - .lwt_retain = 1, - .lwt_msg_len = (int)(lwt_disconnected.length()), - .keepalive = keepalive, - .disable_auto_reconnect = false, // Reconnection routine active (Default: false) - .buffer_size = 1536, // size of MQTT send/receive buffer (Default: 1024) - .reconnect_timeout_ms = 15000, // Try to reconnect to broker (Default: 10000ms) - .network_timeout_ms = 20000, // Network Timeout (Default: 10000ms) - .message_retransmit_timeout = 3000 // Time after message resent when broker not acknowledged (QoS1, QoS2) - }; ->>>>>>> master if (user.length() && password.length()){ mqtt_cfg.credentials.username = user.c_str(); diff --git a/code/components/jomjol_tfliteclass/CTfLiteClass.cpp b/code/components/jomjol_tfliteclass/CTfLiteClass.cpp index 1d3dcc31..1f8d1daf 100644 --- a/code/components/jomjol_tfliteclass/CTfLiteClass.cpp +++ b/code/components/jomjol_tfliteclass/CTfLiteClass.cpp @@ -258,11 +258,7 @@ bool CTfLiteClass::ReadFileToModel(std::string _fn) LogFile.WriteHeapInfo("CTLiteClass::Alloc modelfile start"); #endif -<<<<<<< HEAD modelfile = (unsigned char*)psram_get_shared_model_memory(); -======= - modelfile = (unsigned char*)malloc_psram_heap(std::string(TAG) + "->modelfile", size, MALLOC_CAP_SPIRAM); ->>>>>>> master if(modelfile != NULL) { @@ -315,15 +311,9 @@ CTfLiteClass::CTfLiteClass() this->modelfile = NULL; this->interpreter = nullptr; this->input = nullptr; -<<<<<<< HEAD this->output = nullptr; this->kTensorArenaSize = TENSOR_ARENA_SIZE; this->tensor_arena = (uint8_t*)psram_get_shared_tensor_arena_memory(); -======= - this->output = nullptr; - this->kTensorArenaSize = 800 * 1024; /// according to testfile: 108000 - so far 600;; 2021-09-11: 200 * 1024 - this->tensor_arena = (uint8_t*)malloc_psram_heap(std::string(TAG) + "->tensor_arena", kTensorArenaSize, MALLOC_CAP_SPIRAM); ->>>>>>> master } @@ -332,12 +322,7 @@ CTfLiteClass::~CTfLiteClass() delete this->interpreter; delete this->error_reporter; -<<<<<<< HEAD psram_free_shared_tensor_arena_and_model_memory(); -======= - free_psram_heap(std::string(TAG) + "->modelfile", modelfile); - free_psram_heap(std::string(TAG) + "->tensor_arena", this->tensor_arena); ->>>>>>> master } diff --git a/code/sdkconfig.defaults b/code/sdkconfig.defaults index 4ef6266d..e41514a1 100644 --- a/code/sdkconfig.defaults +++ b/code/sdkconfig.defaults @@ -101,14 +101,9 @@ CONFIG_SPIRAM_MALLOC_ALWAYSINTERNAL=16384 CONFIG_SPIRAM_MALLOC_RESERVE_INTERNAL=40960 CONFIG_SPIRAM_CACHE_WORKAROUND=y CONFIG_SPIRAM_IGNORE_NOTFOUND=y -<<<<<<< HEAD CONFIG_SPIRAM_TRY_ALLOCATE_WIFI_LWIP=y CONFIG_SPIRAM_ALLOW_BSS_SEG_EXTERNAL_MEMORY=y -======= -#CONFIG_SPIRAM_TRY_ALLOCATE_WIFI_LWIP=y => Leads to memory fragmentation, see https://github.com/jomjol/AI-on-the-edge-device/issues/2200 -#CONFIG_SPIRAM_ALLOW_BSS_SEG_EXTERNAL_MEMORY=y => Leads to memory fragmentation, see https://github.com/jomjol/AI-on-the-edge-device/issues/2200 ->>>>>>> master CONFIG_ESP_INT_WDT_TIMEOUT_MS=300 From a20fec10945645533cd6227d44a42f312c5e9725 Mon Sep 17 00:00:00 2001 From: CaCO3 Date: Sun, 23 Apr 2023 16:50:04 +0200 Subject: [PATCH 40/87] merge conflicts --- Changelog.md | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/Changelog.md b/Changelog.md index ad2a08fa..1b63fa03 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,8 +1,4 @@ -<<<<<<< HEAD ## [15.2.0] - 2023-04-23 -======= -## [15.1.1] - 2023-03-23 ->>>>>>> master ### Update Procedure @@ -10,7 +6,6 @@ Update Procedure see [online documentation](https://jomjol.github.io/AI-on-the-e ### Changes -<<<<<<< HEAD For a full list of changes see [Full list of changes](https://github.com/jomjol/AI-on-the-edge-device/compare/v15.1.1...v15.2.0) #### Added @@ -48,12 +43,6 @@ For a full list of changes see [Full list of changes](https://github.com/jomjol/ #### Added -======= -For a full list of changes see [Full list of changes](https://github.com/jomjol/AI-on-the-edge-device/compare/v15.1.0...v15.1.1) - -#### Added - ->>>>>>> master - [#2206](https://github.com/jomjol/AI-on-the-edge-device/pull/2206) Log PSRAM usage - [#2216](https://github.com/jomjol/AI-on-the-edge-device/pull/2216) Log MQTT connection refused reasons @@ -977,10 +966,7 @@ External Illumination - Initial Version -<<<<<<< HEAD [15.2.0]: https://github.com/jomjol/AI-on-the-edge-device/compare/v15.1.1...v15.1.2 -======= ->>>>>>> master [15.1.1]: https://github.com/jomjol/AI-on-the-edge-device/compare/v15.1.0...v15.1.1 [15.1.0]: https://github.com/jomjol/AI-on-the-edge-device/compare/v15.0.3...v15.1.0 [15.0.3]: https://github.com/jomjol/AI-on-the-edge-device/compare/v14.0.3...v15.0.3 From a636ce3679c62c4ef8721e38cff5552b66924cbb Mon Sep 17 00:00:00 2001 From: CaCO3 Date: Sun, 23 Apr 2023 16:58:25 +0200 Subject: [PATCH 41/87] update changelog --- Changelog.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Changelog.md b/Changelog.md index 1b63fa03..f5f26c67 100644 --- a/Changelog.md +++ b/Changelog.md @@ -12,11 +12,15 @@ For a full list of changes see [Full list of changes](https://github.com/jomjol/ - [#2286](https://github.com/jomjol/AI-on-the-edge-device/pull/2286) Implement a camera livestream handler - [#2252](https://github.com/jomjol/AI-on-the-edge-device/pull/2252) Set prevalue using MQTT + set prevalue to RAW value (REST+MQTT) +- [#2319](https://github.com/jomjol/AI-on-the-edge-device/pull/2319) Extend InfluxDBv1 with individual topic names +- [#](https://github.com/jomjol/AI-on-the-edge-device/pull/) +- [#](https://github.com/jomjol/AI-on-the-edge-device/pull/) #### Changed - [#2285](https://github.com/jomjol/AI-on-the-edge-device/pull/2285) Re-implemented PSRAM usage - [#2325](https://github.com/jomjol/AI-on-the-edge-device/pull/2325) Keep MainFlowTask alive to handle reboot +- [#2233](https://github.com/jomjol/AI-on-the-edge-device/pull/2233) Remove trailing slash in influxDBv1 - [#2305](https://github.com/jomjol/AI-on-the-edge-device/pull/2305) Migration of PlatformIO `5.2.0` to `6.1.0` (resp. ESP IDF from `4.4.2` to `5.0.1`) - Various cleanup and refactoring From 6defcf8d4cd0ab4e0000865cd03922576b5d33cb Mon Sep 17 00:00:00 2001 From: CaCO3 Date: Sun, 23 Apr 2023 17:27:06 +0200 Subject: [PATCH 42/87] Update Changelog.md --- Changelog.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/Changelog.md b/Changelog.md index f5f26c67..fe75c863 100644 --- a/Changelog.md +++ b/Changelog.md @@ -13,8 +13,6 @@ For a full list of changes see [Full list of changes](https://github.com/jomjol/ - [#2286](https://github.com/jomjol/AI-on-the-edge-device/pull/2286) Implement a camera livestream handler - [#2252](https://github.com/jomjol/AI-on-the-edge-device/pull/2252) Set prevalue using MQTT + set prevalue to RAW value (REST+MQTT) - [#2319](https://github.com/jomjol/AI-on-the-edge-device/pull/2319) Extend InfluxDBv1 with individual topic names -- [#](https://github.com/jomjol/AI-on-the-edge-device/pull/) -- [#](https://github.com/jomjol/AI-on-the-edge-device/pull/) #### Changed From 9b1a83c8b41b280981d8ff83bc795ffc9070dd2b Mon Sep 17 00:00:00 2001 From: CaCO3 Date: Thu, 27 Apr 2023 22:30:12 +0200 Subject: [PATCH 43/87] Fix alignment mark PSRAM issue (#2357) * Add lock for shared PSRAM memory, use it for the relevant round steps and the Alignment Mark Task * only allow taking a new image for the Alignment Mark while round got completed * show success/denial of Alignment Mark Update Request * . --------- Co-authored-by: CaCO3 --- .../ClassFlowTakeImage.cpp | 2 + .../jomjol_flowcontroll/MainFlowControl.cpp | 34 +++++++---- code/components/jomjol_helper/psram.cpp | 56 ++++++++++++++++--- code/components/jomjol_helper/psram.h | 3 +- sd-card/html/edit_alignment.html | 16 +++--- sd-card/html/edit_reference.html | 2 +- sd-card/html/readconfigcommon.js | 30 ++++++++-- 7 files changed, 109 insertions(+), 34 deletions(-) diff --git a/code/components/jomjol_flowcontroll/ClassFlowTakeImage.cpp b/code/components/jomjol_flowcontroll/ClassFlowTakeImage.cpp index bafd53f7..e4e11854 100644 --- a/code/components/jomjol_flowcontroll/ClassFlowTakeImage.cpp +++ b/code/components/jomjol_flowcontroll/ClassFlowTakeImage.cpp @@ -218,6 +218,8 @@ bool ClassFlowTakeImage::doFlow(string zwtime) LogFile.WriteHeapInfo("ClassFlowTakeImage::doFlow - After RemoveOldLogs"); #endif + psram_deinit_shared_memory_for_take_image_step(); + return true; } diff --git a/code/components/jomjol_flowcontroll/MainFlowControl.cpp b/code/components/jomjol_flowcontroll/MainFlowControl.cpp index 974920a4..46883d8f 100644 --- a/code/components/jomjol_flowcontroll/MainFlowControl.cpp +++ b/code/components/jomjol_flowcontroll/MainFlowControl.cpp @@ -657,20 +657,32 @@ esp_err_t handler_editflow(httpd_req_t *req) string out2 = out.substr(0, out.length() - 4) + "_org.jpg"; - CAlignAndCutImage *caic = new CAlignAndCutImage("cutref", in); - caic->CutAndSave(out2, x, y, dx, dy); - delete caic; + if ((*flowctrl.getActStatus() == "Flow finished") && psram_init_shared_memory_for_take_image_step()) { + LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Taking image for Alignment Mark Update..."); - CImageBasis *cim = new CImageBasis("cutref", out2); - if (enhance) - { - cim->Contrast(90); + CAlignAndCutImage *caic = new CAlignAndCutImage("cutref", in); + caic->CutAndSave(out2, x, y, dx, dy); + delete caic; + + CImageBasis *cim = new CImageBasis("cutref", out2); + if (enhance) + { + cim->Contrast(90); + } + + cim->SaveToFile(out); + delete cim; + + psram_deinit_shared_memory_for_take_image_step(); + + + zw = "CutImage Done"; + } + else { + LogFile.WriteToFile(ESP_LOG_WARN, TAG, "Taking image for Alignment Mark not possible while device is busy with a round!"); + zw = "Device Busy"; } - cim->SaveToFile(out); - delete cim; - - zw = "CutImage Done"; httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*"); httpd_resp_send(req, zw.c_str(), zw.length()); diff --git a/code/components/jomjol_helper/psram.cpp b/code/components/jomjol_helper/psram.cpp index 8c7dcabb..1bc0254a 100644 --- a/code/components/jomjol_helper/psram.cpp +++ b/code/components/jomjol_helper/psram.cpp @@ -9,6 +9,7 @@ using namespace std; void *shared_region = NULL; uint32_t allocatedBytesForSTBI = 0; +std::string sharedMemoryInUseFor = ""; /** Reserve a large block in the PSRAM which will be shared between the different steps. @@ -33,9 +34,24 @@ bool reserve_psram_shared_region(void) { /******************************************************************* * Memory used in Take Image (STBI) *******************************************************************/ -void psram_init_shared_memory_for_take_image_step(void) { - LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Init shared memory for step 'Take Image' (STBI buffers)"); +bool psram_init_shared_memory_for_take_image_step(void) { + if (sharedMemoryInUseFor != "") { + LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Shared memory in PSRAM already in use for " + sharedMemoryInUseFor + "!"); + return false; + } + + LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Init shared memory for step 'Take Image' (STBI buffers)"); allocatedBytesForSTBI = 0; + sharedMemoryInUseFor = "TakeImage"; + + return true; +} + + +void psram_deinit_shared_memory_for_take_image_step(void) { + LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Deinit shared memory for step 'Take Image' (STBI buffers)"); + allocatedBytesForSTBI = 0; + sharedMemoryInUseFor = ""; } @@ -45,7 +61,7 @@ void *psram_reserve_shared_stbi_memory(size_t size) { if (size >= 100000) { if ((allocatedBytesForSTBI + size) > TENSOR_ARENA_SIZE + MAX_MODEL_SIZE) { // Check if it still fits in the shared region LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Shared memory in PSRAM too small (STBI) to fit additional " + - std::to_string(size) + "bytes! Available: " + std::to_string(TENSOR_ARENA_SIZE + MAX_MODEL_SIZE - allocatedBytesForSTBI) + " bytes!"); + std::to_string(size) + " bytes! Available: " + std::to_string(TENSOR_ARENA_SIZE + MAX_MODEL_SIZE - allocatedBytesForSTBI) + " bytes!"); return NULL; } @@ -83,14 +99,21 @@ void psram_free_shared_stbi_memory(void *p) { * During this step we only use the shared part of the PSRAM * for the tmpImage. *******************************************************************/ -void *psram_reserve_shared_tmp_image_memory(void) { +void *psram_reserve_shared_tmp_image_memory(void) { + if (sharedMemoryInUseFor != "") { + LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Shared memory in PSRAM already in use for " + sharedMemoryInUseFor + "!"); + return NULL; + } + LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Allocating tmpImage (" + std::to_string(IMAGE_SIZE) + " bytes, use shared memory in PSRAM)..."); + sharedMemoryInUseFor = "Aligning"; return shared_region; // Use 1th part of the shared memory for the tmpImage (only user) } void psram_free_shared_temp_image_memory(void) { LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Shared memory used for tmpImage (PSRAM, part of shared memory) is free again"); + sharedMemoryInUseFor = ""; } @@ -103,18 +126,33 @@ void psram_free_shared_temp_image_memory(void) { * Tensor Arena. Therefore we do not need to monitor the usage. *******************************************************************/ void *psram_get_shared_tensor_arena_memory(void) { - LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Allocating Tensor Arena (" + std::to_string(TENSOR_ARENA_SIZE) + " bytes, use shared memory in PSRAM)..."); - return shared_region; // Use 1th part of the shared memory for Tensor + if ((sharedMemoryInUseFor == "") || (sharedMemoryInUseFor == "Digitalization_Model")) { + sharedMemoryInUseFor = "Digitalization_Tensor"; + LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Allocating Tensor Arena (" + std::to_string(TENSOR_ARENA_SIZE) + " bytes, use shared memory in PSRAM)..."); + return shared_region; // Use 1th part of the shared memory for Tensor + } + else { + LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Shared memory in PSRAM already in use for " + sharedMemoryInUseFor + "!"); + return NULL; + } } void *psram_get_shared_model_memory(void) { - LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Allocating Model memory (" + std::to_string(MAX_MODEL_SIZE) + " bytes, use shared memory in PSRAM)..."); - return (uint8_t *)shared_region + TENSOR_ARENA_SIZE; // Use 2nd part of the shared memory (after Tensor Arena) for the model + if ((sharedMemoryInUseFor == "") || (sharedMemoryInUseFor == "Digitalization_Tensor")) { + sharedMemoryInUseFor = "Digitalization_Model"; + LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Allocating Model memory (" + std::to_string(MAX_MODEL_SIZE) + " bytes, use shared memory in PSRAM)..."); + return (uint8_t *)shared_region + TENSOR_ARENA_SIZE; // Use 2nd part of the shared memory (after Tensor Arena) for the model + } + else { + LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Shared memory in PSRAM already in use for " + sharedMemoryInUseFor + "!"); + return NULL; + } } void psram_free_shared_tensor_arena_and_model_memory(void) { + sharedMemoryInUseFor = ""; LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Shared memory used for Tensor Arena and model (PSRAM, part of shared memory) is free again"); } @@ -156,7 +194,7 @@ void *calloc_psram_heap(std::string name, size_t n, size_t size, uint32_t caps) ptr = heap_caps_calloc(n, size, caps); if (ptr != NULL) { - LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Allocated " + to_string(size) + " bytes in PSRAM for '" + name + "'"); + LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Allocated " + to_string(size) + " bytes in PSRAM for '" + name + "'"); } else { LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Failed to allocate " + to_string(size) + " bytes in PSRAM for '" + name + "'!"); diff --git a/code/components/jomjol_helper/psram.h b/code/components/jomjol_helper/psram.h index 4c85bf26..880e883a 100644 --- a/code/components/jomjol_helper/psram.h +++ b/code/components/jomjol_helper/psram.h @@ -9,7 +9,8 @@ bool reserve_psram_shared_region(void); /* Memory used in Take Image Step */ -void psram_init_shared_memory_for_take_image_step(void); +bool psram_init_shared_memory_for_take_image_step(void); +void psram_deinit_shared_memory_for_take_image_step(void); void *psram_reserve_shared_stbi_memory(size_t size); void *psram_reallocate_shared_stbi_memory(void *ptr, size_t newsize); void psram_free_shared_stbi_memory(void *p); diff --git a/sd-card/html/edit_alignment.html b/sd-card/html/edit_alignment.html index 41e5fbca..434f1db7 100644 --- a/sd-card/html/edit_alignment.html +++ b/sd-card/html/edit_alignment.html @@ -73,7 +73,7 @@ select { x: dx: - + y: @@ -133,7 +133,7 @@ function SaveToConfig(){ WriteConfigININew(); UpdateConfigReference(domainname) SaveConfigToServer(domainname); - firework.launch('Configuration got updated. It will get applied after the next reboot!', 'success', 5000); + firework.launch('Reference Marks got updated. The change will get applied after the next reboot!', 'success', 5000); } function EnhanceContrast(){ @@ -144,8 +144,9 @@ function EnhanceContrast(){ refInfo[aktindex]["dy"] = document.getElementById("refdy").value; enhanceCon = true; - MakeContrastImageZW(refInfo[aktindex], enhanceCon, domainname); - UpdateReference(); + if (MakeContrastImageZW(refInfo[aktindex], enhanceCon, domainname)) { + UpdateReference(); + } } function UpdateReference(){ @@ -250,9 +251,10 @@ function dataURLtoBlob(dataurl) { refInfo[aktindex]["y"] = document.getElementById("refy").value; refInfo[aktindex]["dx"] = document.getElementById("refdx").value; refInfo[aktindex]["dy"] = document.getElementById("refdy").value; - MakeRefZW(refInfo[aktindex], domainname); - UpdateReference(); - document.getElementById("enhancecontrast").disabled = false; + if (MakeRefZW(refInfo[aktindex], domainname)) { + UpdateReference(); + document.getElementById("enhancecontrast").disabled = false; + } } function drawGrid(){ diff --git a/sd-card/html/edit_reference.html b/sd-card/html/edit_reference.html index 62a8235d..a39ee759 100644 --- a/sd-card/html/edit_reference.html +++ b/sd-card/html/edit_reference.html @@ -272,7 +272,7 @@ table { SaveCanvasToImage(canvas, "/config/reference.jpg", true, getDomainname()); showReference(param); UpdatePage(); - firework.launch('Reference got updated. It will get applied after the next reboot!', 'success', 5000); + firework.launch('Reference got saved. It will get applied after the next reboot!', 'success', 5000); } } diff --git a/sd-card/html/readconfigcommon.js b/sd-card/html/readconfigcommon.js index 0f9c83fe..f256df8e 100644 --- a/sd-card/html/readconfigcommon.js +++ b/sd-card/html/readconfigcommon.js @@ -252,11 +252,21 @@ function MakeContrastImageZW(zw, _enhance, _domainname){ var xhttp = new XMLHttpRequest(); try { xhttp.open("GET", url, false); - xhttp.send(); } + xhttp.send(); + } catch (error) { // firework.launch('Deleting Config.ini failed!', 'danger', 30000); } + + if (xhttp.responseText == "CutImage Done") { + firework.launch('Reference Image Contrast got enhanced.', 'success', 5000); + return true; + } + else { + firework.launch("Device is busy, please try again when the round got completed!", 'warning', 10000); + return false; + } } @@ -268,13 +278,23 @@ function MakeRefZW(zw, _domainname){ var xhttp = new XMLHttpRequest(); try { xhttp.open("GET", url, false); - xhttp.send(); } + xhttp.send(); + } catch (error) { // firework.launch('Deleting Config.ini failed!', 'danger', 30000); } - _filetarget2 = zw["name"].replace("/config/", "/img_tmp/"); -// _filetarget2 = _filetarget2.replace(".jpg", "_org.jpg"); - FileCopyOnServer(_filetarget, _filetarget2, _domainname); + + if (xhttp.responseText == "CutImage Done") { + _filetarget2 = zw["name"].replace("/config/", "/img_tmp/"); + // _filetarget2 = _filetarget2.replace(".jpg", "_org.jpg"); + FileCopyOnServer(_filetarget, _filetarget2, _domainname); + firework.launch('Reference Image got updated.', 'success', 5000); + return true; + } + else { + firework.launch("Device is busy, please try again when the round got completed!", 'warning', 10000); + return false; + } } From fc719da0aef9d7f5bbeb459fa5aa05b2a17f4169 Mon Sep 17 00:00:00 2001 From: CaCO3 Date: Thu, 27 Apr 2023 22:34:04 +0200 Subject: [PATCH 44/87] Update Changelog.md (#2360) --- Changelog.md | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/Changelog.md b/Changelog.md index fe75c863..d96f6652 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,9 +1,11 @@ +## [15.2.1] - 2023-04-27 + +#### Fixed + - [#2357](https://github.com/jomjol/AI-on-the-edge-device/pull/2357) Fix Alignment Mark issue + + ## [15.2.0] - 2023-04-23 -### Update Procedure - -Update Procedure see [online documentation](https://jomjol.github.io/AI-on-the-edge-device-docs/Installation/#update-ota-over-the-air) - ### Changes For a full list of changes see [Full list of changes](https://github.com/jomjol/AI-on-the-edge-device/compare/v15.1.1...v15.2.0) @@ -35,10 +37,6 @@ For a full list of changes see [Full list of changes](https://github.com/jomjol/ ## [15.1.1] - 2023-03-23 -### Update Procedure - -Update Procedure see [online documentation](https://jomjol.github.io/AI-on-the-edge-device-docs/Installation/#update-ota-over-the-air) - ### Changes For a full list of changes see [Full list of changes](https://github.com/jomjol/AI-on-the-edge-device/compare/v15.1.0...v15.1.1) @@ -968,7 +966,8 @@ External Illumination - Initial Version -[15.2.0]: https://github.com/jomjol/AI-on-the-edge-device/compare/v15.1.1...v15.1.2 +[15.2.1]: https://github.com/jomjol/AI-on-the-edge-device/compare/v15.2.0...v15.2.1 +[15.2.0]: https://github.com/jomjol/AI-on-the-edge-device/compare/v15.1.1...v15.2.0 [15.1.1]: https://github.com/jomjol/AI-on-the-edge-device/compare/v15.1.0...v15.1.1 [15.1.0]: https://github.com/jomjol/AI-on-the-edge-device/compare/v15.0.3...v15.1.0 [15.0.3]: https://github.com/jomjol/AI-on-the-edge-device/compare/v14.0.3...v15.0.3 From d7a507ca058efeffb413f881607485c95dd61093 Mon Sep 17 00:00:00 2001 From: Slider0007 Date: Mon, 1 May 2023 14:19:31 +0200 Subject: [PATCH 45/87] Update WebUI --- sd-card/html/backup.html | 59 +- sd-card/html/common.js | 4 +- sd-card/html/data.html | 44 +- sd-card/html/edit_alignment.html | 207 +- sd-card/html/edit_analog.html | 509 ++-- sd-card/html/edit_check.html | 136 -- sd-card/html/edit_config.html | 124 +- sd-card/html/edit_config_param.html | 2068 ++++++++++------- sd-card/html/edit_digits.html | 448 ++-- sd-card/html/edit_explain_0.html | 55 +- sd-card/html/edit_explain_7.html | 74 + ...plain_6.html => edit_explain_7_abort.html} | 41 +- sd-card/html/edit_reference.html | 332 ++- sd-card/html/explain_1.html | 33 +- sd-card/html/explain_2.html | 21 +- sd-card/html/explain_3.html | 23 +- sd-card/html/explain_4.html | 22 +- sd-card/html/explain_5.html | 26 +- sd-card/html/explain_6.html | 24 +- sd-card/html/explain_7.html | 1 + sd-card/html/file_server.html | 271 ++- sd-card/html/firework.css | 6 +- sd-card/html/firework.js | 2 +- sd-card/html/graph.html | 78 +- sd-card/html/index.html | 205 +- sd-card/html/info.html | 643 +++-- sd-card/html/log.html | 29 +- sd-card/html/ota_page.html | 30 +- sd-card/html/overview.html | 177 +- sd-card/html/prevalue_set.html | 196 +- sd-card/html/readconfigcommon.js | 22 +- sd-card/html/readconfigparam.js | 47 +- sd-card/html/reboot_page.html | 15 +- sd-card/html/setup.html | 420 +++- sd-card/html/style.css | 50 +- sd-card/html/test.html | 33 - sd-card/html/testcnn.html | 1 - sd-card/html/timezones.html | 2 +- sd-card/html/wlan_config.html | 12 +- 39 files changed, 3961 insertions(+), 2529 deletions(-) delete mode 100644 sd-card/html/edit_check.html create mode 100644 sd-card/html/edit_explain_7.html rename sd-card/html/{edit_explain_6.html => edit_explain_7_abort.html} (50%) create mode 100644 sd-card/html/explain_7.html delete mode 100644 sd-card/html/test.html delete mode 100644 sd-card/html/testcnn.html diff --git a/sd-card/html/backup.html b/sd-card/html/backup.html index c55c3d3d..04af87f3 100644 --- a/sd-card/html/backup.html +++ b/sd-card/html/backup.html @@ -1,42 +1,41 @@ - + - -Backup/Restore Configuration - + Backup/Restore Configuration + - + .button { + padding: 5px 10px; + width: 205px; + font-size: 16px; + } + -

Backup Configuration

-

With the following action the config folder on the SD-card gets zipped and provided as a download.

+

Backup Configuration

+

With the following action the config folder on the SD-card gets zipped and provided as a download.

- -

-
-

Restore Configuration

-

Use the File Server to upload individual files.

+ +

+
+

Restore Configuration

+

Use the File Server to upload individual files.

@@ -105,7 +104,7 @@ function fetchFiles(urls, filesData, index, retry, zipFilename) { // console.log(url + " started (" + index + "/" + urls.length + ")"); if (retry == 0) { - setStatus(" - " + getFilenameFromUrl(urls[index]) + " (" + index + "/" + urls.length + ")..."); + setStatus(" - " + getFilenameFromUrl(urls[index]) + " (" + (index+1) + "/" + urls.length + ")..."); } else { setStatus("   Retrying (" + retry + ")..."); diff --git a/sd-card/html/common.js b/sd-card/html/common.js index bd0eea35..4919d435 100644 --- a/sd-card/html/common.js +++ b/sd-card/html/common.js @@ -145,8 +145,8 @@ function compareVersions() { console.log("FW Hash: " + fWGitHash + ", Web UI Hash: " + webUiHash); if (fWGitHash != webUiHash) { - firework.launch("The Version of the Web Interface (" + webUiHash + - ") does not match the Firmware Version (" + + firework.launch("The version of the web interface (" + webUiHash + + ") does not match the firmware version (" + fWGitHash + ")! It is suggested to keep them on the same version!", 'warning', 30000); } } diff --git a/sd-card/html/data.html b/sd-card/html/data.html index a592241c..09f9885b 100644 --- a/sd-card/html/data.html +++ b/sd-card/html/data.html @@ -1,17 +1,26 @@ - + + + Data Viewer + + -

Todays Data

-

Last part of Todays Data

+

Data Viewer

+

Today's latest data

- - - - + + + +
-



Loading Data file, please wait...
+



Loading data file, please wait...
diff --git a/sd-card/html/edit_alignment.html b/sd-card/html/edit_alignment.html index 434f1db7..fe89abc4 100644 --- a/sd-card/html/edit_alignment.html +++ b/sd-card/html/edit_alignment.html @@ -1,101 +1,135 @@ - + - -Alignment Marks - - - - - - + select { + padding: 3px 5px; + display: inline-block; + border: 1px solid #ccc; + font-size: 16px; + margin-right: 10px; + min-width: 100px; + vertical-align: middle; + } + + .button { + padding: 5px 10px; + width: 205px; + font-size: 16px; + } + + th, td { + padding: 5px 5px 5px 0px; + } + + table { + width: 660px; + padding: 5px; + } + + + + -

Alignment Marks

-

On this page you define two Reference Marks. - See https://jomjol.github.io/AI-on-the-edge-device-docs/Alignment/ for explanations.

-

After saving the Reference Marks, you can define the digit resp. analog ROI's.
- Only after those steps a reboot is required.

+

Alignment Marker

+
+ CLICK HERE for usage description. More infos in documentation: + Alignment + +

+ Two alignment marker with clear contour and proper contrast are needed to identify unique "fix points" on the image. + The marker area should be not be part of the variable area of ROI evaluation. Please find more information in documenation: + Alignment +

+

+ Select an alignment marker area using drag and dop feature by mouse operation or by manually entering the coordinates and sizes in the fields below the image. + After you selected a suitable first alignment marker area, push the "Update Marker" button. Switch to second alignment marker with "Marker" + and repeat the procedure. +

+

+ After definition of both alignment marker is completed don't forget to save with the "Save New Marker" button!
+ NOTE: There is no need to perform a reboot after every saving or step. It's sufficient to reboot after all configuration steps + (reference image, alignment, ROI configuration) are completed to activate new configuration. +

+
+
+ + + + + - - -
- - - - - + - + - - - + + + - - - -
Select Reference: + Marker: Storage Path/Name: Filename:
x: dx:
y: dy:
Original Image:Reference Image:Selected Image Area:Resulting Marker:
+ + + + + Reference Image: + + + + + + + + - - - - -
-

Proceed to update the digit resp. analog ROI's when you are done.

@@ -130,10 +164,15 @@ function ChangeSelection(){ } function SaveToConfig(){ - WriteConfigININew(); - UpdateConfigReference(domainname) - SaveConfigToServer(domainname); - firework.launch('Reference Marks got updated. The change will get applied after the next reboot!', 'success', 5000); + if (confirm("Are you sure you want to save the new alignment marker configuration?")) { + WriteConfigININew(); + UpdateConfigReference(domainname) + SaveConfigToServer(domainname); + document.getElementById("savemarker").disabled = true; + document.getElementById("enhancecontrast").disabled = true; + + firework.launch('Alignment marker saved. They will get applied after next reboot', 'success', 5000); + } } function EnhanceContrast(){ @@ -146,6 +185,7 @@ function EnhanceContrast(){ enhanceCon = true; if (MakeContrastImageZW(refInfo[aktindex], enhanceCon, domainname)) { UpdateReference(); + document.getElementById("enhancecontrast").disabled = true; } } @@ -174,7 +214,6 @@ function UpdateReference(){ document.getElementById("refy").value = refInfo[aktindex]["y"]; rect.startX = document.getElementById("refx").value; rect.startY = document.getElementById("refy").value; - document.getElementById("enhancecontrast").disabled = true; draw(); } @@ -215,14 +254,27 @@ function dataURLtoBlob(dataurl) { return { top: Math.round(top), left: Math.round(left) }; } + + /* hash #description open the details part of the page */ + function openDescription() { + if(window.location.hash) { + var hash = window.location.hash.substring(1); //Puts hash in variable, and removes the # character + if(hash == 'description') + document.getElementById("desc_details").open = true; + } + } + - - function init() { + function init() { + openDescription(); domainname = getDomainname(); loadConfig(domainname); ParseConfig(); param = getConfigParameters(); + document.getElementById("savemarker").disabled = true; + document.getElementById("enhancecontrast").disabled = true; + canvas.addEventListener('mousedown', mouseDown, false); canvas.addEventListener('mouseup', mouseUp, false); canvas.addEventListener('mousemove', mouseMove, false); @@ -235,6 +287,8 @@ function dataURLtoBlob(dataurl) { drawImage(); } + + function drawImage(){ var canvas = document.getElementById('canvas'); var context = canvas.getContext('2d'); @@ -254,6 +308,7 @@ function dataURLtoBlob(dataurl) { if (MakeRefZW(refInfo[aktindex], domainname)) { UpdateReference(); document.getElementById("enhancecontrast").disabled = false; + document.getElementById("savemarker").disabled = false; } } diff --git a/sd-card/html/edit_analog.html b/sd-card/html/edit_analog.html index d1641370..a3993d78 100644 --- a/sd-card/html/edit_analog.html +++ b/sd-card/html/edit_analog.html @@ -1,142 +1,221 @@ - + - - -Analog ROI's + + Analog ROI - + + + + -#div2{ - background-color:#777; - margin-bottom:20px; -} -.disabledDiv { - pointer-events: none; - opacity: 0.4; -} - - - - -

Analog ROI's

-

On this page you define ROI's for the analog counters. - See https://jomjol.github.io/AI-on-the-edge-device-docs/ROI-Configuration/ for explanations.

- -

- -
- - - - -
+

Analog ROI

+
+ CLICK HERE for usage description. More infos in documentation: + ROI Configuration + +

+ Region Of Interest (ROI) for analog pointer counter can be defined on this page. If no analog pointer counter need to be + processed, disable analog pointer counter processing by deselecting "Analog ROI Processing". +

+

+ By default one number sequence (a number seqence contains of 1-x digit ROIs + 1-x analog counter ROIs which are processed together) is + predefined and already selected in the drop down "Number Sequence". If you need more than one number sequence additional + one's can be added with the buttons next to the drop down. Each number sequence will be processed separately. +

+

+ Using drag and drop by mouse of by manually entering the parameters into the given fields the analog ROIs can be positined to the analog pointer + counters on the reference image. To have proper ROI definition please check the documentation: + ROI Configuration. It's very important to be + really precise to have reliable processing. With the drop down "ROI" you can change between the different ROIs in the selected + number sequence. To create new ROIs use "New ROI". +

+

+ The order of the ROIs defines the position (and therefore the multiplication factor) within the reading sequence. The position + in the number sequence can be changed with the buttons "Move ROI Lower" and "Move ROI Higher". The multiplication factor which is + shown below the ROI drop down is the multiplication factor of pure position/order in number sequence and the factor right-hand side to this is + the additionally corrected by decimal shift setting (configuration, expert parameter, default: 0). +

+

+ After definition of digit ROIs is completed don't forget to save with the "Save Config" button!
+ NOTE: There is no need to perform a reboot after every saving or step. It's sufficient to reboot after all configuration steps + (reference image, alignment, ROI configuration) are completed to activate new configuration. +

+
+
-

- - - Number: - - - - - -
+

+ +

- - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - -
x: Δx:
y: Δy:
-
+ +
+ + + + + + + + + + + + + + + + + +
Number Sequence:
+ +
+ +
+ + + + + + + + + + + + + + + + + + + + + + + +
ROI:
Multiplier:
+ (only based on order) +
Multiplier:
+ (order + decimal shift: ) +
- - - - - -
-
+ + + + + + + x: + Δx: + + + + y: + Δy: + + + + + + + +
+ + + + + + + + + + + + + + + + +
Reference Image:
@@ -158,16 +237,15 @@ th, td { lockSizes = false; domainname = getDomainname(); - -function doReboot() { - if (confirm("Are you sure you want to reboot? Did you save your changes?")) { - var stringota = getDomainname() + "/reboot"; - window.location = stringota; - window.location.href = stringota; - window.location.assign(stringota); - window.location.replace(stringota); + function doReboot() { + if (confirm("Are you sure you want to reboot? Did you save your changes?")) { + var stringota = getDomainname() + "/reboot"; + window.location = stringota; + window.location.href = stringota; + window.location.assign(stringota); + window.location.replace(stringota); + } } -} function EnDisableAnalog() { isEnabled = document.getElementById("Category_Analog_enabled").checked; @@ -186,9 +264,9 @@ function EnDisableAnalog() { sah1(document.getElementById("div1"), !isEnabled); - cofcat["Analog"]["enabled"] = isEnabled; - + document.getElementById("saveroi").disabled = false; + if (isEnabled) { UpdateROIs(); @@ -212,6 +290,10 @@ function onNameChange(){ } function deleteROI(){ + if (!confirm("Delete the selected ROI?")) { + return; //break out of the function early because prompt was aborted + } + ROIInfo.splice(aktindex, 1); if (aktindex > ROIInfo.length - 1){ aktindex = ROIInfo.length - 1; @@ -220,18 +302,21 @@ function deleteROI(){ draw(); } -function newROI(){ +function newROI() { var sel = document.getElementById("Numbers_value1"); var _number= sel.options[sel.selectedIndex].text; sel = document.getElementById("index"); var _roialt= sel.options[sel.selectedIndex].text; - var _roinew = prompt("Please enter name of new ROI", "name"); + var _roinew = prompt("Please enter a name for the new ROI", "name"); + if (_roinew === null) { + return; //break out of the function early because prompt was aborted + } if (ROIInfo.length > 0) - erg = CreateROI(_number, "analog", sel.selectedIndex, _roinew, 1, 1, ROIInfo[aktindex]["dx"], ROIInfo[aktindex]["dy"], ROIInfo[aktindex]["CCW"]=="true"); + erg = CreateROI(_number, "analog", sel.selectedIndex, _roinew, 15, 30, ROIInfo[aktindex]["dx"], ROIInfo[aktindex]["dy"], ROIInfo[aktindex]["CCW"]=="true"); else - erg = CreateROI(_number, "analog", sel.selectedIndex, _roinew, 1, 1, 30, 30, false); + erg = CreateROI(_number, "analog", sel.selectedIndex, _roinew, 15, 30, 30, 30, false); if (erg != "") firework.launch(erg, 'danger', 30000); @@ -275,7 +360,9 @@ function changeCCW(){ ROIInfo[aktindex]["CCW"] = "true"; else ROIInfo[aktindex]["CCW"] = "false"; - UpdateROIs(); + + UpdateROIs(); + } function ChangeSelection(){ @@ -285,11 +372,47 @@ function ChangeSelection(){ } function SaveToConfig(){ -// _zwcat = getConfigCategory(); - cofcat["Analog"]["enabled"] = document.getElementById("Category_Analog_enabled").checked; - WriteConfigININew(); - SaveConfigToServer(domainname); - firework.launch('Configuration got updated. It will get applied after the next reboot!', 'success', 5000); + if (confirm("Are you sure you want to save the new analog ROI configuration?")) { + //_zwcat = getConfigCategory(); + cofcat["Analog"]["enabled"] = document.getElementById("Category_Analog_enabled").checked; + WriteConfigININew(); + SaveConfigToServer(domainname); + UpdateROIs(); + document.getElementById("saveroi").disabled = true; + + firework.launch('Configuration saved. It will get applied after next reboot', 'success', 5000); + } +} + + +function ShowMultiplier(){ + var decimalShift = 0; + var multiplier = multiplier_decshift = aktindex+1-Number(decimalShift); + var fixedDecimals = fixedDecimals_decshift = aktindex+1; + var NumberInfo = getNUMBERInfo(); + + var sel = document.getElementById("Numbers_value1"); + var _number= sel.options[sel.selectedIndex].text; + var NumbersIndex = 0; + for (var i = 0; i < NumberInfo.length; ++i) + if (NumberInfo[i]["name"] == _number) + NumbersIndex = i; + + if (NumberInfo[NumbersIndex]["PostProcessing"]["DecimalShift"]["enabled"]) { + decimalShift = NumberInfo[NumbersIndex]["PostProcessing"]["DecimalShift"]["value1"]; + document.getElementById("decimalShift").value=decimalShift; + multiplier_decshift = aktindex+1-Number(decimalShift); + fixedDecimals_decshift = fixedDecimals_decshift-Number(decimalShift); // set to fixed decimals to avoid rounding issues + + if (fixedDecimals_decshift < 0) + fixedDecimals_decshift = 0; + } + else { + document.getElementById("decimalShift").value=0; + } + + document.getElementById("multiplier").value="x" + Number(10 ** (-1*multiplier)).toFixed(fixedDecimals); + document.getElementById("multiplier_decshift").value="x" + Number(10 ** (-1*multiplier_decshift)).toFixed(fixedDecimals_decshift); } @@ -305,19 +428,28 @@ function UpdateROIs(_sel){ { document.getElementById("Category_Analog_enabled").checked = false; EnDisableAnalog(); - firework.launch('Analog ROIs are disabled - please enable first (Check box top left)', 'warning', 10000); + firework.launch('Analog ROI processing is disabled. Activate with checkbox if needed', 'warning', 10000); return; } if (ROIInfo.length == 0){ - firework.launch('There are no ROIs defined. Please first create a new ROI ("New ROIs ...")', 'danger', 10000); + firework.launch('No analog ROIs defined in selected number sequence', 'warning', 10000); document.getElementById("newROI").disabled = false; document.getElementById("deleteROI").disabled = true; - document.getElementById("index").disabled = true; - document.getElementById("saveroi").disabled = true; document.getElementById("renameROI").disabled = true; + document.getElementById("index").disabled = true; + document.getElementById("multiplier").style.display = "none"; + document.getElementById("multiplier_decshift").style.display = "none"; + document.getElementById("refx").disabled = true; + document.getElementById("refdx").disabled = true; + document.getElementById("refy").disabled = true; + document.getElementById("refdy").disabled = true; + document.getElementById("lockSizes").disabled = true; + document.getElementById("lockAspectRatio").disabled = true; + document.getElementById("CCW").disabled = true; document.getElementById("moveNext").disabled = true; document.getElementById("movePrevious").disabled = true; + document.getElementById("saveroi").disabled = false; return; } else @@ -326,6 +458,15 @@ function UpdateROIs(_sel){ document.getElementById("deleteROI").disabled = false; document.getElementById("renameROI").disabled = false; document.getElementById("index").disabled = false; + document.getElementById("multiplier").style.display = ""; + document.getElementById("multiplier_decshift").style.display = ""; + document.getElementById("refx").disabled = false; + document.getElementById("refdx").disabled = false; + document.getElementById("refy").disabled = false; + document.getElementById("refdy").disabled = false; + document.getElementById("lockSizes").disabled = false; + document.getElementById("lockAspectRatio").disabled = false; + document.getElementById("CCW").disabled = false; document.getElementById("saveroi").disabled = false; } @@ -335,7 +476,7 @@ function UpdateROIs(_sel){ } if (aktindex > ROIInfo.length) - aktindex = ROIInfo.length; + aktindex = ROIInfo.length-1; for (var i = 0; i < ROIInfo.length; ++i){ var option = document.createElement("option"); @@ -358,7 +499,9 @@ function UpdateROIs(_sel){ document.getElementById("moveNext").disabled = false; if (aktindex == (ROIInfo.length-1)){ document.getElementById("moveNext").disabled = true; - } + } + + ShowMultiplier(); document.getElementById("lockAspectRatio").checked = lockAspectRatio; document.getElementById("lockSizes").checked = lockSizes; @@ -373,8 +516,7 @@ function UpdateROIs(_sel){ rect.startY = ROIInfo[aktindex]["y"]; rect.w = ROIInfo[aktindex]["dx"]; rect.h = ROIInfo[aktindex]["dy"]; - draw(); - + draw(); } function loadCanvas(dataURL) { @@ -404,10 +546,20 @@ function UpdateROIs(_sel){ var left = box.left + scrollLeft - clientLeft; return { top: Math.round(top), left: Math.round(left) }; } - - + + + /* hash #description open the details part of the page */ + function openDescription() { + if(window.location.hash) { + var hash = window.location.hash.substring(1); //Puts hash in variable, and removes the # character + if(hash == 'description') + document.getElementById("desc_details").open = true; + } + } + function init() { + openDescription(); domainname = getDomainname(); canvas.addEventListener('mousedown', mouseDown, false); canvas.addEventListener('mouseup', mouseUp, false); @@ -440,11 +592,12 @@ function UpdateROIs(_sel){ console.log("Not all ROI have the same dX and dY, unticking the sync checkbox!"); } + document.getElementById("saveroi").disabled = true; drawImage(); draw(); } - + function drawImage(){ var canvas = document.getElementById('canvas'); var context = canvas.getContext('2d'); @@ -483,10 +636,13 @@ function UpdateNUMBERS(_sel){ function renameNumber(){ var sel = document.getElementById("Numbers_value1"); - var _delte= sel.options[sel.selectedIndex].text; - var _numbernew = prompt("Please enter new name", _delte); + var _delete= sel.options[sel.selectedIndex].text; + var _numbernew = prompt("Please enter a new name for the selected number sequence", _delete); + if (_numbernew === null) { + return; //break out of the function early because prompt was aborted + } - erg = RenameNUMBER(_delte, _numbernew); + erg = RenameNUMBER(_delete, _numbernew); if (erg != "") firework.launch(erg, 'danger', 30000); else @@ -494,7 +650,10 @@ function renameNumber(){ } function newNumber(){ - var _numbernew = prompt("Please enter name of new number", "name"); + var _numbernew = prompt("Please enter a name for the new number sequence", "name"); + if (_numbernew === null) { + return; //break out of the function early because prompt was aborted + } erg = CreateNUMBER(_numbernew); if (erg != "") @@ -505,10 +664,13 @@ function newNumber(){ function removeNumber(){ - if (confirm("This will remove the number complete (analog and digital).\nIf you only want to remove the digital ROIs, please use \"Delete ROIs\".\nDo you want to proceed?")) { + if (confirm("The entire number sequence will be removed (digit + analog parts). " + + "To remove single ROI of the number sequence, use \"Delete ROI\" instead.\n" + + "Do you really want to proceed?")) + { var sel = document.getElementById("Numbers_value1"); - var _delte= sel.options[sel.selectedIndex].text; - erg = DeleteNUMBER(_delte); + var _delete= sel.options[sel.selectedIndex].text; + erg = DeleteNUMBER(_delete); if (erg != "") firework.launch(erg, 'danger', 30000); UpdateNUMBERS(); @@ -576,8 +738,12 @@ function drawTextBG(context, txt, x, y, padding) { var dx = parseInt(ROIInfo[_nb].dx) + parseInt(lw); var dy = parseInt(ROIInfo[_nb].dy) + parseInt(lw); context.strokeRect(x0, y0, dx, dy); - drawTextBG(context, ROIInfo[_nb]["name"], x0+dx/2-0.5, y0-13, 5); - + + if (ROIInfo[_nb]["CCW"] != "true") + drawTextBG(context, ROIInfo[_nb]["name"], x0+dx/2-0.5, y0-13, 5); + else + drawTextBG(context, ROIInfo[_nb]["name"]+" (CCW)", x0+dx/2-0.5, y0-13, 5); + lw = 1; var x0 = parseInt(ROIInfo[_nb].x) - parseInt(lw/2); var y0 = parseInt(ROIInfo[_nb].y) - parseInt(lw/2); @@ -602,8 +768,13 @@ function drawTextBG(context, txt, x, y, padding) { var y0 = parseInt(rect.startY) - parseInt(lw/2); var dx = parseInt(rect.w) + parseInt(lw); var dy = parseInt(rect.h) + parseInt(lw); - context.strokeRect(x0, y0, dx, dy); - drawTextBG(context, ROIInfo[aktindex]["name"], x0+dx/2, y0-11, 5); + context.strokeRect(x0, y0, dx, dy); + + if (ROIInfo[aktindex]["CCW"] != "true") + drawTextBG(context, ROIInfo[aktindex]["name"], x0+dx/2, y0-11, 5); + else + drawTextBG(context, ROIInfo[aktindex]["name"] + " (CCW)", x0+dx/2, y0-11, 5); + context.lineWidth = 1; context.beginPath(); context.arc(x0+dx/2, y0+dy/2, dx/2, 0, 2 * Math.PI); @@ -706,6 +877,7 @@ function drawTextBG(context, txt, x, y, padding) { rect.startY = document.getElementById("refy").value; draw(); } + document.getElementById("saveroi").disabled = false; } function valuemanualchangeddx(){ @@ -721,6 +893,7 @@ function drawTextBG(context, txt, x, y, padding) { rect.startY = document.getElementById("refy").value; draw(); } + document.getElementById("saveroi").disabled = false; } @@ -730,8 +903,10 @@ function drawTextBG(context, txt, x, y, padding) { sel = document.getElementById("index"); var _roialt= sel.options[sel.selectedIndex].text; - - var _roinew = prompt("Please enter new name", _roialt); + var _roinew = prompt("Please enter a new name for the selected ROI", _roialt); + if (_roinew === null) { + return; //break out of the function early because prompt was aborted + } erg = RenameROI(_number, "analog", _roialt, _roinew); if (erg != "") @@ -742,13 +917,15 @@ function drawTextBG(context, txt, x, y, padding) { function numberChanged() { + aktindex = 0; UpdateROIs(); } - init(); + + diff --git a/sd-card/html/edit_check.html b/sd-card/html/edit_check.html deleted file mode 100644 index 27ca5012..00000000 --- a/sd-card/html/edit_check.html +++ /dev/null @@ -1,136 +0,0 @@ - - - - - -Check - - - - - - - - - - - - - -
Result:
- - -

- -

-

-
-

-

- Takes up to 2 Minutes! -

- Digits and Analog recognition not yet implemented. -

- -

-

- -

-
- - - - - - - - diff --git a/sd-card/html/edit_config.html b/sd-card/html/edit_config.html index 475b875e..bbac9047 100644 --- a/sd-card/html/edit_config.html +++ b/sd-card/html/edit_config.html @@ -1,25 +1,24 @@ - + - Edit Config - + @@ -28,59 +27,60 @@ textarea { - - - - - - - - - - - -

Config.ini:

- -
+ +

Configuration - "Config.ini" Editor

+ +
+ +
- - - - + + + + + LoadConfigNeu(); + + diff --git a/sd-card/html/edit_config_param.html b/sd-card/html/edit_config_param.html index 39895746..4c853b3e 100644 --- a/sd-card/html/edit_config_param.html +++ b/sd-card/html/edit_config_param.html @@ -1,108 +1,165 @@ - + - -Edit Config - +Configuration + @@ -116,50 +173,85 @@ textarea {

Configuration

-

The documentation of each parameter can be shown with hovering over the icon.
- The latest version of the documentation is available on jomjol.github.io/AI-on-the-edge-device-docs/Parameters.

- - -

- @@ -1410,13 +1648,6 @@ textarea { function LoadConfigNeu() { - var isInitialSetup = getParameterByName('InitialSetup'); - if (isInitialSetup === 'true') - { - document.getElementById("reboot").style.display = "none"; - } - - domainname = getDomainname(); if (!loadConfig(domainname)) { firework.launch('Configuration could not be loaded! Please reload the page!', 'danger', 30000); @@ -1434,42 +1665,43 @@ function LoadConfigNeu() { document.getElementById("divall").style.display = ''; } + function InitIndivParameter() { - NUMBERS = getNUMBERInfo(); + NUMBERS = getNUMBERInfo(); var _index = document.getElementById("Numbers_value1"); while (_index.length) _index.remove(0); - var _indexInflux = document.getElementById("NumbersInfluxDBv2_value1"); + var _indexInflux = document.getElementById("NumbersInfluxDBv2_value1"); while (_indexInflux.length) - _indexInflux.remove(0); + _indexInflux.remove(0); - var _indexInfluxv1 = document.getElementById("NumbersInfluxDB_value1"); + var _indexInfluxv1 = document.getElementById("NumbersInfluxDB_value1"); while (_indexInflux.length) - _indexInfluxv1.remove(0); + _indexInfluxv1.remove(0); - for (var i = 0; i < NUMBERS.length; ++i){ - var option = document.createElement("option"); + for (var i = 0; i < NUMBERS.length; ++i){ + var option = document.createElement("option"); option.text = NUMBERS[i]["name"]; option.value = i; _index.add(option); - var optionInflux = document.createElement("option"); + var optionInflux = document.createElement("option"); optionInflux.text = NUMBERS[i]["name"]; optionInflux.value = i; - _indexInflux.add(optionInflux); + _indexInflux.add(optionInflux); - var optionInfluxv1 = document.createElement("option"); + var optionInfluxv1 = document.createElement("option"); optionInfluxv1.text = NUMBERS[i]["name"]; optionInfluxv1.value = i; - _indexInfluxv1.add(optionInfluxv1); + _indexInfluxv1.add(optionInfluxv1); - } + } _index.selectedIndex = 0; - _indexInflux.selectedIndex = 0; - _indexInfluxv1.selectedIndex = 0; + _indexInflux.selectedIndex = 0; + _indexInfluxv1.selectedIndex = 0; } @@ -1478,418 +1710,438 @@ function getParameterByName(name, url = window.location.href) { var regex = new RegExp('[?&]' + name + '(=([^&#]*)|&|#|$)'), results = regex.exec(url); - if (!results) return null; + if (!results) return null; if (!results[2]) return ''; - return decodeURIComponent(results[2].replace(/\+/g, ' ')); + return decodeURIComponent(results[2].replace(/\+/g, ' ')); } + function WriteParameter(_param, _category, _cat, _name, _optional, _number = -1){ - let anzpara; - try { - anzpara = _param[_cat][_name].anzParam; - } - catch (error) { - firework.launch("Parameter '" + _name + "' in category '" + _cat + "' is unknown!", 'danger', 30000); - return; - } + let anzpara; + try { + anzpara = _param[_cat][_name].anzParam; + } + catch (error) { + firework.launch("Parameter '" + _name + "' in category '" + _cat + "' is unknown!", 'danger', 30000); + return; + } - if (_number > -1) { - if ((NUMBERS[_number] == undefined) || (NUMBERS[_number][_cat] == undefined) || (NUMBERS[_number][_cat][_name] == undefined)) - return; + if (_number > -1) { + if ((NUMBERS[_number] == undefined) || (NUMBERS[_number][_cat] == undefined) || (NUMBERS[_number][_cat][_name] == undefined)) + return; - if (_optional) { - document.getElementById(_cat+"_"+_name+"_enabled").checked = NUMBERS[_number][_cat][_name]["enabled"]; - for (var j = 1; j <= anzpara; ++j) { - document.getElementById(_cat+"_"+_name+"_value"+j).disabled = !NUMBERS[_number][_cat][_name]["enabled"]; - } - } - document.getElementById(_cat+"_"+_name+"_text").style="color:black;" - setEnabled(_cat+"_"+_name, true); + if (_optional) { + document.getElementById(_cat+"_"+_name+"_enabled").checked = NUMBERS[_number][_cat][_name]["enabled"]; + for (var j = 1; j <= anzpara; ++j) { + document.getElementById(_cat+"_"+_name+"_value"+j).disabled = !NUMBERS[_number][_cat][_name]["enabled"]; + } + } + document.getElementById(_cat+"_"+_name+"_text").style.color = "black" + setEnabled(_cat+"_"+_name, true); - for (var j = 1; j <= anzpara; ++j) { - let element = document.getElementById(_cat+"_"+_name+"_value"+j); - if (element.tagName.toLowerCase() == "select") { - var textToFind = NUMBERS[_number][_cat][_name]["value"+j]; - if (textToFind == undefined) - continue; + for (var j = 1; j <= anzpara; ++j) { + let element = document.getElementById(_cat+"_"+_name+"_value"+j); + if (element.tagName.toLowerCase() == "select") { + var textToFind = NUMBERS[_number][_cat][_name]["value"+j]; + if (textToFind == undefined) + continue; - _isFound = false; - element.selectedIndex = -1; - for (var i = 0; i < element.options.length; i++) { - if (element.options[i].value.toLowerCase() === textToFind.toLowerCase()) { - element.selectedIndex = i; - _isFound = true; - break; - } - } - if (!_isFound) - { - _zw_txt = "In the selected field the value '" + textToFind + "' in the parameter '"; - _zw_txt = _zw_txt + _cat + "' in the field '" + _name + "' is invalid. PLEASE CHECK BEFORE SAVING!"; - firework.launch(_zw_txt, 'warning', 10000); - } - } - else if ((element.getAttribute("type") != null) && (element.getAttribute("type").toLowerCase() == "checkbox")) { - element.checked = NUMBERS[_number][_cat][_name]["value"+j] == "true"; - } - else { - element.value = NUMBERS[_number][_cat][_name]["value"+j]; - } - } - } - else { - if (_optional) { - document.getElementById(_cat+"_"+_name+"_enabled").checked = _param[_cat][_name]["enabled"]; - for (var j = 1; j <= anzpara; ++j) { - document.getElementById(_cat+"_"+_name+"_value"+j).disabled = !_param[_cat][_name]["enabled"]; - } - } - document.getElementById(_cat+"_"+_name+"_text").style="color:black;" - setEnabled(_cat+"_"+_name, true); + _isFound = false; + element.selectedIndex = -1; + for (var i = 0; i < element.options.length; i++) { + if (element.options[i].value.toLowerCase() === textToFind.toLowerCase()) { + element.selectedIndex = i; + _isFound = true; + break; + } + } + if (!_isFound) + { + _zw_txt = "In the selected field the value '" + textToFind + "' in the parameter '"; + _zw_txt = _zw_txt + _cat + "' in the field '" + _name + "' is invalid. PLEASE CHECK BEFORE SAVING!"; + firework.launch(_zw_txt, 'warning', 10000); + } + } + else if ((element.getAttribute("type") != null) && (element.getAttribute("type").toLowerCase() == "checkbox")) { + element.checked = NUMBERS[_number][_cat][_name]["value"+j] == "true"; + } + else { + element.value = NUMBERS[_number][_cat][_name]["value"+j]; + } + } + } + else { + if (_optional) { + document.getElementById(_cat+"_"+_name+"_enabled").checked = _param[_cat][_name]["enabled"]; + for (var j = 1; j <= anzpara; ++j) { + document.getElementById(_cat+"_"+_name+"_value"+j).disabled = !_param[_cat][_name]["enabled"]; + } + } + document.getElementById(_cat+"_"+_name+"_text").style.color = "black" + setEnabled(_cat+"_"+_name, true); - for (var j = 1; j <= anzpara; ++j) { - let element = document.getElementById(_cat+"_"+_name+"_value"+j); - if (element.tagName.toLowerCase() == "select") { - var textToFind = _param[_cat][_name]["value"+j]; - if (textToFind == undefined) - continue; - - _isFound = false; - element.selectedIndex = -1; - for (var i = 0; i < element.options.length; i++) { - if (element.options[i].value.toLowerCase() === textToFind.toLowerCase()) { - element.selectedIndex = i; - _isFound = true; - break; - } - } - if (!_isFound) - { - _zw_txt = "In the selected field the value '" + textToFind + "' in the section '"; - _zw_txt = _zw_txt + _cat + "' in the field '" + _name + "' is invalid. PLEASE CHECK BEFORE SAVING!"; - firework.launch(_zw_txt, 'warning', 10000); - } + for (var j = 1; j <= anzpara; ++j) { + let element = document.getElementById(_cat+"_"+_name+"_value"+j); + if (element.tagName.toLowerCase() == "select") { + var textToFind = _param[_cat][_name]["value"+j]; + if (textToFind == undefined) + continue; + + _isFound = false; + element.selectedIndex = -1; + for (var i = 0; i < element.options.length; i++) { + if (element.options[i].value.toLowerCase() === textToFind.toLowerCase()) { + element.selectedIndex = i; + _isFound = true; + break; + } + } + if (!_isFound) + { + _zw_txt = "In the selected field the value '" + textToFind + "' in the section '"; + _zw_txt = _zw_txt + _cat + "' in the field '" + _name + "' is invalid. PLEASE CHECK BEFORE SAVING!"; + firework.launch(_zw_txt, 'warning', 10000); + } - } - else if ((element.getAttribute("type") != null) && (element.getAttribute("type").toLowerCase() == "checkbox")) { - element.checked = _param[_cat][_name]["value"+j] == "true"; - } - else { - element.value = _param[_cat][_name]["value"+j]; - } - } - } + } + else if ((element.getAttribute("type") != null) && (element.getAttribute("type").toLowerCase() == "checkbox")) { + element.checked = _param[_cat][_name]["value"+j] == "true"; + } + else { + element.value = _param[_cat][_name]["value"+j]; + } + } + } - ///////////////// am Ende, falls Kategorie als gesamtes nicht ausgewählt --> deaktivieren - if (_category[_cat]["enabled"] == false) - { - if (_optional) { - document.getElementById(_cat+"_"+_name+"_enabled").disabled = true; - for (var j = 1; j <= anzpara; ++j) { - document.getElementById(_cat+"_"+_name+"_value"+j).disabled = true; - } - } - document.getElementById(_cat+"_"+_name+"_text").style="color: gray;" - setEnabled(_cat+"_"+_name, false); - } + ///////////////// am Ende, falls Kategorie als gesamtes nicht ausgewählt --> deaktivieren + if (_category[_cat]["enabled"] == false) + { + if (_optional) { + document.getElementById(_cat+"_"+_name+"_enabled").disabled = true; + for (var j = 1; j <= anzpara; ++j) { + document.getElementById(_cat+"_"+_name+"_value"+j).disabled = true; + } + } + document.getElementById(_cat+"_"+_name+"_text").style="color: gray;" + setEnabled(_cat+"_"+_name, false); + } - EnDisableItem(_category[_cat]["enabled"], _param, _category, _cat, _name, _optional, _number); + EnDisableItem(_category[_cat]["enabled"], _param, _category, _cat, _name, _optional, _number); } + function InvertEnableItem(_cat, _param) { - _zw = _cat + "_" + _param + "_enabled"; - _isOn = document.getElementById(_zw).checked; + _zw = _cat + "_" + _param + "_enabled"; + _isOn = document.getElementById(_zw).checked; - _color = "color: rgb(122, 122, 122);"; - if (_isOn) { - _color = "color:black;"; - } + _color = "rgb(122, 122, 122)"; + if (_isOn) { + _color = "black"; + } - _zw = _cat + "_" + _param + "_text"; - document.getElementById(_zw).disabled = !_isOn; - document.getElementById(_zw).style = _color; + _zw = _cat + "_" + _param + "_text"; + document.getElementById(_zw).disabled = !_isOn; + document.getElementById(_zw).style.color = _color; - setEnabled(_cat + "_" + _param, _isOn); + setEnabled(_cat + "_" + _param, _isOn); - for (var j = 1; j <= param[_cat][_param]["anzParam"]; ++j) { - document.getElementById(_cat+"_"+_param+"_value"+j).disabled = !_isOn; - document.getElementById(_cat+"_"+_param+"_value"+j).style=_color; - } + for (var j = 1; j <= param[_cat][_param]["anzParam"]; ++j) { + document.getElementById(_cat+"_"+_param+"_value"+j).disabled = !_isOn; + document.getElementById(_cat+"_"+_param+"_value"+j).style.color = _color; + } } + function setEnabled(className, enabled) { - _color = "color: rgb(122, 122, 122);"; - if (enabled) { - _color = "color:black;"; - } + _color = "rgb(122, 122, 122)"; + if (enabled) { + _color = "black"; + } - let elements = document.getElementsByClassName(className); - for (i = 0; i < elements.length; i++) { - if (enabled) { - elements[i].classList.remove("disabled"); - } else { - elements[i].classList.add("disabled"); - } + let elements = document.getElementsByClassName(className); + for (i = 0; i < elements.length; i++) { + if (enabled) { + elements[i].classList.remove("disabled"); + } else { + elements[i].classList.add("disabled"); + } - let inputs = elements[i].getElementsByTagName("input"); - for (j = 0; j < inputs.length; j++) { - if (inputs[j].id.endsWith("_enabled")) - continue; + let inputs = elements[i].getElementsByTagName("input"); + for (j = 0; j < inputs.length; j++) { + if (inputs[j].id.endsWith("_enabled")) + continue; - inputs[j].style = _color - if (enabled) { - inputs[j].removeAttribute("disabled"); - } else { - inputs[j].setAttribute("disabled", "disabled"); - } - } - } + inputs[j].style.color = _color; + if (enabled) { + inputs[j].removeAttribute("disabled"); + } else { + inputs[j].setAttribute("disabled", "disabled"); + } + } + } } + function setVisible(className, visible) { - let elements = document.getElementsByClassName(className); - for (i = 0; i < elements.length; i++) { - if (visible) { - elements[i].classList.remove("hidden"); - } else { - elements[i].classList.add("hidden"); - } - } + let elements = document.getElementsByClassName(className); + for (i = 0; i < elements.length; i++) { + if (visible) { + elements[i].classList.remove("hidden"); + } else { + elements[i].classList.add("hidden"); + } + } } + function EnDisableItem(_status, _param, _category, _cat, _name, _optional, _number = -1) { - _status = _category[_cat]["enabled"]; + _status = _category[_cat]["enabled"]; - _color = "color: rgb(122, 122, 122);"; - if (_status) { - _color = "color:black;"; - } + _color = "rgb(122, 122, 122)"; + if (_status) { + _color = "black"; + } - if (_optional) { - document.getElementById(_cat+"_"+_name+"_enabled").disabled = !_status; - document.getElementById(_cat+"_"+_name+"_enabled").style=_color; - } + if (_optional) { + document.getElementById(_cat+"_"+_name+"_enabled").disabled = !_status; + document.getElementById(_cat+"_"+_name+"_enabled").style.color = _color; + } - if (_number == -1){ - if (!_param[_cat][_name]["enabled"]) { - _status = false; - _color = "color: rgb(122, 122, 122);"; - } - } - else - if (!NUMBERS[_number][_cat][_name]["enabled"]) { - _status = false; - _color = "color: rgb(122, 122, 122);"; - } + if (_number == -1){ + if (!_param[_cat][_name]["enabled"]) { + _status = false; + _color = "rgb(122, 122, 122)"; + } + } + else + if (!NUMBERS[_number][_cat][_name]["enabled"]) { + _status = false; + _color = "rgb(122, 122, 122)"; + } - document.getElementById(_cat+"_"+_name+"_text").disabled = !_status; - document.getElementById(_cat+"_"+_name+"_text").style = _color; + document.getElementById(_cat+"_"+_name+"_text").disabled = !_status; + document.getElementById(_cat+"_"+_name+"_text").style.color = _color; - setEnabled(_cat+"_"+_name, _status); + setEnabled(_cat+"_"+_name, _status); - for (var j = 1; j <= _param[_cat][_name]["anzParam"]; ++j) { - document.getElementById(_cat+"_"+_name+"_value"+j).disabled = !_status; - document.getElementById(_cat+"_"+_name+"_value"+j).style=_color; - } + for (var j = 1; j <= _param[_cat][_name]["anzParam"]; ++j) { + document.getElementById(_cat+"_"+_name+"_value"+j).disabled = !_status; + document.getElementById(_cat+"_"+_name+"_value"+j).style.color = _color; + } } function ReadParameter(_param, _cat, _name, _optional, _number = -1){ - if (_number > -1) { - if (_cat == "Digits") - _cat = "digit"; - if (_cat == "Analog") - _cat = "analog"; + if (_number > -1) { + if (_cat == "Digits") + _cat = "digit"; + if (_cat == "Analog") + _cat = "analog"; - if ((NUMBERS[_number] == undefined) || (NUMBERS[_number][_cat] == undefined) || (NUMBERS[_number][_cat][_name] == undefined)) - return; + if ((NUMBERS[_number] == undefined) || (NUMBERS[_number][_cat] == undefined) || (NUMBERS[_number][_cat][_name] == undefined)) + return; - if (_optional) { - NUMBERS[_number][_cat][_name]["enabled"] = document.getElementById(_cat+"_"+_name+"_enabled").checked; - } + if (_optional) { + NUMBERS[_number][_cat][_name]["enabled"] = document.getElementById(_cat+"_"+_name+"_enabled").checked; + } - for (var j = 1; j <= _param[_cat][_name]["anzParam"]; ++j) { - let element = document.getElementById(_cat+"_"+_name+"_value"+j); - if (element.tagName.toLowerCase() == "select") { - NUMBERS[_number][_cat][_name]["value"+j] = element.selectedIndex > -1 ? element.options[element.selectedIndex].value : ""; - } - else if ((element.getAttribute("type") != null) && (element.getAttribute("type").toLowerCase() == "checkbox")) { - NUMBERS[_number][_cat][_name]["value"+j] = element.checked; - } - else { - if ((NUMBERS[_number][_cat][_name].checkRegExList != null) && (NUMBERS[_number][_cat][_name].checkRegExList[j-1] != null)) { - if (!element.value.match(NUMBERS[_cat][_name].checkRegExList[j-1])) { - element.classList.add("invalid-input"); - } else { - element.classList.remove("invalid-input"); - } - } - NUMBERS[_number][_cat][_name]["value"+j] = element.value; - } - } - } - else - { - if (_optional) { - _param[_cat][_name]["enabled"] = document.getElementById(_cat+"_"+_name+"_enabled").checked; - } + for (var j = 1; j <= _param[_cat][_name]["anzParam"]; ++j) { + let element = document.getElementById(_cat+"_"+_name+"_value"+j); + if (element.tagName.toLowerCase() == "select") { + NUMBERS[_number][_cat][_name]["value"+j] = element.selectedIndex > -1 ? element.options[element.selectedIndex].value : ""; + } + else if ((element.getAttribute("type") != null) && (element.getAttribute("type").toLowerCase() == "checkbox")) { + NUMBERS[_number][_cat][_name]["value"+j] = element.checked; + } + else { + if ((NUMBERS[_number][_cat][_name].checkRegExList != null) && (NUMBERS[_number][_cat][_name].checkRegExList[j-1] != null)) { + if (!element.value.match(NUMBERS[_cat][_name].checkRegExList[j-1])) { + element.classList.add("invalid-input"); + } else { + element.classList.remove("invalid-input"); + } + } + NUMBERS[_number][_cat][_name]["value"+j] = element.value; + } + } + } + else + { + if (_optional) { + _param[_cat][_name]["enabled"] = document.getElementById(_cat+"_"+_name+"_enabled").checked; + } - for (var j = 1; j <= _param[_cat][_name]["anzParam"]; ++j) { - let element = document.getElementById(_cat+"_"+_name+"_value"+j); - if (element.tagName.toLowerCase() == "select") { - _param[_cat][_name]["value"+j] = element.selectedIndex > -1 ? element.options[element.selectedIndex].value : ""; - } - else if ((element.getAttribute("type") != null) && (element.getAttribute("type").toLowerCase() == "checkbox")) { - _param[_cat][_name]["value"+j] = element.checked; - } - else { - if ((_param[_cat][_name].checkRegExList != null) && (_param[_cat][_name].checkRegExList[j-1] != null)) { - if (!element.value.match(_param[_cat][_name].checkRegExList[j-1])) { - element.classList.add("invalid-input"); - } else { - element.classList.remove("invalid-input"); - } - } - _param[_cat][_name]["value"+j] = element.value; - } - } - } + for (var j = 1; j <= _param[_cat][_name]["anzParam"]; ++j) { + let element = document.getElementById(_cat+"_"+_name+"_value"+j); + if (element.tagName.toLowerCase() == "select") { + _param[_cat][_name]["value"+j] = element.selectedIndex > -1 ? element.options[element.selectedIndex].value : ""; + } + else if ((element.getAttribute("type") != null) && (element.getAttribute("type").toLowerCase() == "checkbox")) { + _param[_cat][_name]["value"+j] = element.checked; + } + else { + if ((_param[_cat][_name].checkRegExList != null) && (_param[_cat][_name].checkRegExList[j-1] != null)) { + if (!element.value.match(_param[_cat][_name].checkRegExList[j-1])) { + element.classList.add("invalid-input"); + } else { + element.classList.remove("invalid-input"); + } + } + _param[_cat][_name]["value"+j] = element.value; + } + } + } } + function UpdateInputIndividual(sel) { - if (NUNBERSAkt != -1) - { - ReadParameter(param, "PostProcessing", "DecimalShift", true, NUNBERSAkt) - ReadParameter(param, "PostProcessing", "AnalogDigitalTransitionStart", true, NUNBERSAkt) - ReadParameter(param, "PostProcessing", "MaxRateValue", true, NUNBERSAkt) - ReadParameter(param, "PostProcessing", "MaxRateType", true, NUNBERSAkt) - ReadParameter(param, "PostProcessing", "ExtendedResolution", false, NUNBERSAkt) - ReadParameter(param, "PostProcessing", "IgnoreLeadingNaN", false, NUNBERSAkt) - ReadParameter(param, "PostProcessing", "AllowNegativeRates", false, NUNBERSAkt) - ReadParameter(param, "InfluxDB", "Field", true, NUNBERSAkt) - ReadParameter(param, "InfluxDBv2", "Field", true, NUNBERSAkt) - ReadParameter(param, "InfluxDB", "Measurement", true, NUNBERSAkt) - ReadParameter(param, "InfluxDBv2", "Measurement", true, NUNBERSAkt) - } + if (NUNBERSAkt != -1) + { + ReadParameter(param, "PostProcessing", "DecimalShift", true, NUNBERSAkt); + ReadParameter(param, "PostProcessing", "AnalogDigitalTransitionStart", true, NUNBERSAkt); + ReadParameter(param, "PostProcessing", "MaxRateValue", true, NUNBERSAkt); + ReadParameter(param, "PostProcessing", "MaxRateType", true, NUNBERSAkt); + ReadParameter(param, "PostProcessing", "ExtendedResolution", false, NUNBERSAkt); + ReadParameter(param, "PostProcessing", "IgnoreLeadingNaN", false, NUNBERSAkt); + ReadParameter(param, "PostProcessing", "AllowNegativeRates", false, NUNBERSAkt); + ReadParameter(param, "InfluxDB", "Field", true, NUNBERSAkt); + ReadParameter(param, "InfluxDBv2", "Field", true, NUNBERSAkt); + ReadParameter(param, "InfluxDB", "Measurement", true, NUNBERSAkt); + ReadParameter(param, "InfluxDBv2", "Measurement", true, NUNBERSAkt); + } // var sel = document.getElementById("Numbers_value1"); - NUNBERSAkt = sel.selectedIndex; - WriteParameter(param, category, "PostProcessing", "DecimalShift", true, NUNBERSAkt); - WriteParameter(param, category, "PostProcessing", "AnalogDigitalTransitionStart", true, NUNBERSAkt); - WriteParameter(param, category, "PostProcessing", "MaxRateValue", true, NUNBERSAkt); - WriteParameter(param, category, "PostProcessing", "MaxRateType", true, NUNBERSAkt); - WriteParameter(param, category, "PostProcessing", "ExtendedResolution", false, NUNBERSAkt); - WriteParameter(param, category, "PostProcessing", "IgnoreLeadingNaN", false, NUNBERSAkt); - WriteParameter(param, category, "PostProcessing", "AllowNegativeRates", false, NUNBERSAkt); - WriteParameter(param, category, "InfluxDB", "Field", true, NUNBERSAkt); - WriteParameter(param, category, "InfluxDBv2", "Field", true, NUNBERSAkt); - WriteParameter(param, category, "InfluxDB", "Measurement", true, NUNBERSAkt); - WriteParameter(param, category, "InfluxDBv2", "Measurement", true, NUNBERSAkt); + NUNBERSAkt = sel.selectedIndex; + WriteParameter(param, category, "PostProcessing", "DecimalShift", true, NUNBERSAkt); + WriteParameter(param, category, "PostProcessing", "AnalogDigitalTransitionStart", true, NUNBERSAkt); + WriteParameter(param, category, "PostProcessing", "MaxRateValue", true, NUNBERSAkt); + WriteParameter(param, category, "PostProcessing", "MaxRateType", true, NUNBERSAkt); + WriteParameter(param, category, "PostProcessing", "ExtendedResolution", false, NUNBERSAkt); + WriteParameter(param, category, "PostProcessing", "IgnoreLeadingNaN", false, NUNBERSAkt); + WriteParameter(param, category, "PostProcessing", "AllowNegativeRates", false, NUNBERSAkt); + WriteParameter(param, category, "InfluxDB", "Field", true, NUNBERSAkt); + WriteParameter(param, category, "InfluxDBv2", "Field", true, NUNBERSAkt); + WriteParameter(param, category, "InfluxDB", "Measurement", true, NUNBERSAkt); + WriteParameter(param, category, "InfluxDBv2", "Measurement", true, NUNBERSAkt); } + function UpdateInput() { - document.getElementById("Category_Analog_enabled").checked = category["Analog"]["enabled"]; - document.getElementById("Category_Digits_enabled").checked = category["Digits"]["enabled"]; - document.getElementById("Category_MQTT_enabled").checked = category["MQTT"]["enabled"]; - document.getElementById("Category_GPIO_enabled").checked = category["GPIO"]["enabled"]; - document.getElementById("Category_InfluxDB_enabled").checked = category["InfluxDB"]["enabled"]; - document.getElementById("Category_InfluxDBv2_enabled").checked = category["InfluxDBv2"]["enabled"]; - setVisible("GPIO_item", category["GPIO"]["enabled"]); + document.getElementById("Category_Digits_enabled").checked = category["Digits"]["enabled"]; + setVisible("DigitItem", category["Digits"]["enabled"]); - WriteParameter(param, category, "TakeImage", "RawImagesLocation", true); - WriteParameter(param, category, "TakeImage", "RawImagesRetention", true); - WriteParameter(param, category, "TakeImage", "Demo", false); - WriteParameter(param, category, "TakeImage", "WaitBeforeTakingPicture", false); - WriteParameter(param, category, "TakeImage", "ImageQuality", false); - WriteParameter(param, category, "TakeImage", "Brightness", false); - WriteParameter(param, category, "TakeImage", "Contrast", false); - WriteParameter(param, category, "TakeImage", "Saturation", false); - WriteParameter(param, category, "TakeImage", "LEDIntensity", false); - WriteParameter(param, category, "TakeImage", "ImageSize", false); - WriteParameter(param, category, "TakeImage", "FixedExposure", false); + document.getElementById("Category_Analog_enabled").checked = category["Analog"]["enabled"]; + setVisible("AnalogItem", category["Analog"]["enabled"]); - WriteParameter(param, category, "Alignment", "SearchFieldX", false); - WriteParameter(param, category, "Alignment", "SearchFieldY", false); - WriteParameter(param, category, "Alignment", "AlignmentAlgo", true); + document.getElementById("Category_MQTT_enabled").checked = category["MQTT"]["enabled"]; + setVisible("MQTTItem", category["MQTT"]["enabled"]); - WriteParameter(param, category, "Digits", "CNNGoodThreshold", true); - WriteParameter(param, category, "Digits", "ROIImagesLocation", true); - WriteParameter(param, category, "Digits", "ROIImagesRetention", true); - - WriteParameter(param, category, "Analog", "ROIImagesLocation", true); - WriteParameter(param, category, "Analog", "ROIImagesRetention", true); - - WriteParameter(param, category, "PostProcessing", "PreValueUse", false); - WriteParameter(param, category, "PostProcessing", "PreValueAgeStartup", true); + document.getElementById("Category_GPIO_enabled").checked = category["GPIO"]["enabled"]; + setVisible("GPIO_item", category["GPIO"]["enabled"]); + + document.getElementById("Category_InfluxDB_enabled").checked = category["InfluxDB"]["enabled"]; + setVisible("InfluxDBv1Item", category["InfluxDB"]["enabled"]); + + document.getElementById("Category_InfluxDBv2_enabled").checked = category["InfluxDBv2"]["enabled"]; + setVisible("InfluxDBv2Item", category["InfluxDBv2"]["enabled"]); + + WriteParameter(param, category, "TakeImage", "RawImagesLocation", true); + WriteParameter(param, category, "TakeImage", "RawImagesRetention", true); + WriteParameter(param, category, "TakeImage", "Demo", false); + WriteParameter(param, category, "TakeImage", "WaitBeforeTakingPicture", false); + WriteParameter(param, category, "TakeImage", "ImageQuality", false); + WriteParameter(param, category, "TakeImage", "Brightness", false); + WriteParameter(param, category, "TakeImage", "Contrast", false); + WriteParameter(param, category, "TakeImage", "Saturation", false); + WriteParameter(param, category, "TakeImage", "LEDIntensity", false); + WriteParameter(param, category, "TakeImage", "ImageSize", false); + WriteParameter(param, category, "TakeImage", "FixedExposure", false); + + WriteParameter(param, category, "Alignment", "SearchFieldX", false); + WriteParameter(param, category, "Alignment", "SearchFieldY", false); + WriteParameter(param, category, "Alignment", "AlignmentAlgo", true); + WriteParameter(param, category, "Alignment", "FlipImageSize", false); + WriteParameter(param, category, "Alignment", "InitialMirror", false); + + WriteParameter(param, category, "Digits", "CNNGoodThreshold", true); + WriteParameter(param, category, "Digits", "ROIImagesLocation", true); + WriteParameter(param, category, "Digits", "ROIImagesRetention", true); + + WriteParameter(param, category, "Analog", "ROIImagesLocation", true); + WriteParameter(param, category, "Analog", "ROIImagesRetention", true); + + WriteParameter(param, category, "PostProcessing", "PreValueUse", false); + WriteParameter(param, category, "PostProcessing", "PreValueAgeStartup", true); // WriteParameter(param, category, "PostProcessing", "AllowNegativeRates", true); - WriteParameter(param, category, "PostProcessing", "ErrorMessage", false); - WriteParameter(param, category, "PostProcessing", "CheckDigitIncreaseConsistency", false); + WriteParameter(param, category, "PostProcessing", "ErrorMessage", false); + WriteParameter(param, category, "PostProcessing", "CheckDigitIncreaseConsistency", false); - WriteParameter(param, category, "MQTT", "Uri", true); - WriteParameter(param, category, "MQTT", "MainTopic", true); - WriteParameter(param, category, "MQTT", "ClientID", true); - WriteParameter(param, category, "MQTT", "user", true); - WriteParameter(param, category, "MQTT", "password", true); - WriteParameter(param, category, "MQTT", "RetainMessages", false); - WriteParameter(param, category, "MQTT", "HomeassistantDiscovery", false); - WriteParameter(param, category, "MQTT", "MeterType", true); - - WriteParameter(param, category, "InfluxDB", "Uri", true); - WriteParameter(param, category, "InfluxDB", "Database", true); + WriteParameter(param, category, "MQTT", "Uri", true); + WriteParameter(param, category, "MQTT", "MainTopic", true); + WriteParameter(param, category, "MQTT", "ClientID", true); + WriteParameter(param, category, "MQTT", "user", true); + WriteParameter(param, category, "MQTT", "password", true); + WriteParameter(param, category, "MQTT", "RetainMessages", false); + WriteParameter(param, category, "MQTT", "HomeassistantDiscovery", false); + WriteParameter(param, category, "MQTT", "MeterType", true); + + WriteParameter(param, category, "InfluxDB", "Uri", true); + WriteParameter(param, category, "InfluxDB", "Database", true); // WriteParameter(param, category, "InfluxDB", "Measurement", true); - WriteParameter(param, category, "InfluxDB", "user", true); - WriteParameter(param, category, "InfluxDB", "password", true); + WriteParameter(param, category, "InfluxDB", "user", true); + WriteParameter(param, category, "InfluxDB", "password", true); // WriteParameter(param, category, "InfluxDB", "Field", true); - WriteParameter(param, category, "InfluxDBv2", "Uri", true); - WriteParameter(param, category, "InfluxDBv2", "Database", true); + WriteParameter(param, category, "InfluxDBv2", "Uri", true); + WriteParameter(param, category, "InfluxDBv2", "Database", true); // WriteParameter(param, category, "InfluxDBv2", "Measurement", true); - WriteParameter(param, category, "InfluxDBv2", "Org", true); - WriteParameter(param, category, "InfluxDBv2", "Token", true); + WriteParameter(param, category, "InfluxDBv2", "Org", true); + WriteParameter(param, category, "InfluxDBv2", "Token", true); // WriteParameter(param, category, "InfluxDBv2", "Field", true); - WriteParameter(param, category, "GPIO", "IO0", true); - WriteParameter(param, category, "GPIO", "IO1", true); - WriteParameter(param, category, "GPIO", "IO3", true); - WriteParameter(param, category, "GPIO", "IO4", true); - WriteParameter(param, category, "GPIO", "IO12", true); - WriteParameter(param, category, "GPIO", "IO13", true); - WriteParameter(param, category, "GPIO", "LEDType", false); - WriteParameter(param, category, "GPIO", "LEDNumbers", false); - WriteParameter(param, category, "GPIO", "LEDColor", false); + WriteParameter(param, category, "GPIO", "IO0", true); + WriteParameter(param, category, "GPIO", "IO1", true); + WriteParameter(param, category, "GPIO", "IO3", true); + WriteParameter(param, category, "GPIO", "IO4", true); + WriteParameter(param, category, "GPIO", "IO12", true); + WriteParameter(param, category, "GPIO", "IO13", true); + WriteParameter(param, category, "GPIO", "LEDType", false); + WriteParameter(param, category, "GPIO", "LEDNumbers", false); + WriteParameter(param, category, "GPIO", "LEDColor", false); - WriteParameter(param, category, "AutoTimer", "AutoStart", false); - WriteParameter(param, category, "AutoTimer", "Interval", false); + WriteParameter(param, category, "AutoTimer", "AutoStart", false); + WriteParameter(param, category, "AutoTimer", "Interval", false); - WriteParameter(param, category, "DataLogging", "DataLogActive", false); - WriteParameter(param, category, "DataLogging", "DataFilesRetention", false); + WriteParameter(param, category, "DataLogging", "DataLogActive", false); + WriteParameter(param, category, "DataLogging", "DataFilesRetention", false); - WriteParameter(param, category, "Debug", "LogLevel", false); - WriteParameter(param, category, "Debug", "LogfilesRetention", false); + WriteParameter(param, category, "Debug", "LogLevel", false); + WriteParameter(param, category, "Debug", "LogfilesRetention", false); - WriteParameter(param, category, "System", "TimeZone", true); - WriteParameter(param, category, "System", "Hostname", true); - WriteParameter(param, category, "System", "TimeServer", true); - WriteParameter(param, category, "System", "RSSIThreshold", true); - WriteParameter(param, category, "System", "CPUFrequency", true); + WriteParameter(param, category, "System", "TimeZone", true); + WriteParameter(param, category, "System", "Hostname", true); + WriteParameter(param, category, "System", "TimeServer", true); + WriteParameter(param, category, "System", "RSSIThreshold", true); + WriteParameter(param, category, "System", "CPUFrequency", true); - WriteModelFiles(); + WriteModelFiles(); } + function WriteModelFiles() { - list_tflite = getTFLITEList(); + list_tflite = getTFLITEList(); var _indexDig = document.getElementById("Digits_Model_value1"); var _indexAna = document.getElementById("Analog_Model_value1"); while (_indexDig.length) _indexDig.remove(0); - while (_indexAna.length) + while (_indexAna.length) _indexAna.remove(0); for (var i = 0; i < list_tflite.length; ++i){ @@ -1899,23 +2151,23 @@ function WriteModelFiles() var text = list_tflite[i].replace("/config/", ""); if (list_tflite[i].includes("/dig")) { // Its a digital file, only show in the digital list box - optionDig.text = text; - optionDig.value = list_tflite[i]; - _indexDig.add(optionDig); + optionDig.text = text; + optionDig.value = list_tflite[i]; + _indexDig.add(optionDig); } else if (list_tflite[i].includes("/ana")) { // Its a digital file, only show in the analog list box - optionAna.text = text; - optionAna.value = list_tflite[i]; - _indexAna.add(optionAna); + optionAna.text = text; + optionAna.value = list_tflite[i]; + _indexAna.add(optionAna); } else { // all other files, show in both list boxes - optionDig.text = text; - optionDig.value = list_tflite[i]; - _indexDig.add(optionDig); - - optionAna.text = text; - optionAna.value = list_tflite[i]; - _indexAna.add(optionAna); + optionDig.text = text; + optionDig.value = list_tflite[i]; + _indexDig.add(optionDig); + + optionAna.text = text; + optionAna.value = list_tflite[i]; + _indexAna.add(optionAna); } } @@ -1923,241 +2175,265 @@ function WriteModelFiles() WriteParameter(param, category, "Digits", "Model", false); } + function ReadParameterAll() { - category["Analog"]["enabled"] = document.getElementById("Category_Analog_enabled").checked; - category["Digits"]["enabled"] = document.getElementById("Category_Digits_enabled").checked; - category["MQTT"]["enabled"] = document.getElementById("Category_MQTT_enabled").checked; - category["InfluxDB"]["enabled"] = document.getElementById("Category_InfluxDB_enabled").checked; - category["InfluxDBv2"]["enabled"] = document.getElementById("Category_InfluxDBv2_enabled").checked; - category["GPIO"]["enabled"] = document.getElementById("Category_GPIO_enabled").checked; - - ReadParameter(param, "TakeImage", "RawImagesLocation", true); - ReadParameter(param, "TakeImage", "RawImagesRetention", true); - ReadParameter(param, "TakeImage", "Demo", false); - ReadParameter(param, "TakeImage", "WaitBeforeTakingPicture", false); - ReadParameter(param, "TakeImage", "ImageQuality", false); - ReadParameter(param, "TakeImage", "Brightness", false); - ReadParameter(param, "TakeImage", "Contrast", false); - ReadParameter(param, "TakeImage", "Saturation", false); - ReadParameter(param, "TakeImage", "LEDIntensity", false); - ReadParameter(param, "TakeImage", "ImageSize", false); - ReadParameter(param, "TakeImage", "FixedExposure", false); + category["Analog"]["enabled"] = document.getElementById("Category_Analog_enabled").checked; + category["Digits"]["enabled"] = document.getElementById("Category_Digits_enabled").checked; + category["MQTT"]["enabled"] = document.getElementById("Category_MQTT_enabled").checked; + category["InfluxDB"]["enabled"] = document.getElementById("Category_InfluxDB_enabled").checked; + category["InfluxDBv2"]["enabled"] = document.getElementById("Category_InfluxDBv2_enabled").checked; + category["GPIO"]["enabled"] = document.getElementById("Category_GPIO_enabled").checked; + + ReadParameter(param, "TakeImage", "RawImagesLocation", true); + ReadParameter(param, "TakeImage", "RawImagesRetention", true); + ReadParameter(param, "TakeImage", "Demo", false); + ReadParameter(param, "TakeImage", "WaitBeforeTakingPicture", false); + ReadParameter(param, "TakeImage", "ImageQuality", false); + ReadParameter(param, "TakeImage", "Brightness", false); + ReadParameter(param, "TakeImage", "Contrast", false); + ReadParameter(param, "TakeImage", "Saturation", false); + ReadParameter(param, "TakeImage", "LEDIntensity", false); + ReadParameter(param, "TakeImage", "ImageSize", false); + ReadParameter(param, "TakeImage", "FixedExposure", false); - ReadParameter(param, "Alignment", "SearchFieldX", false); - ReadParameter(param, "Alignment", "SearchFieldY", false); - ReadParameter(param, "Alignment", "AlignmentAlgo", true); + ReadParameter(param, "Alignment", "SearchFieldX", false); + ReadParameter(param, "Alignment", "SearchFieldY", false); + ReadParameter(param, "Alignment", "AlignmentAlgo", true); + ReadParameter(param, "Alignment", "FlipImageSize", false); + ReadParameter(param, "Alignment", "InitialMirror", false); - ReadParameter(param, "Digits", "Model", false); - ReadParameter(param, "Digits", "CNNGoodThreshold", true); - ReadParameter(param, "Digits", "ROIImagesLocation", true); - ReadParameter(param, "Digits", "ROIImagesRetention", true); + ReadParameter(param, "Digits", "Model", false); + ReadParameter(param, "Digits", "CNNGoodThreshold", true); + ReadParameter(param, "Digits", "ROIImagesLocation", true); + ReadParameter(param, "Digits", "ROIImagesRetention", true); - ReadParameter(param, "Analog", "Model", false); - ReadParameter(param, "Analog", "ROIImagesLocation", true); - ReadParameter(param, "Analog", "ROIImagesRetention", true); + ReadParameter(param, "Analog", "Model", false); + ReadParameter(param, "Analog", "ROIImagesLocation", true); + ReadParameter(param, "Analog", "ROIImagesRetention", true); - ReadParameter(param, "PostProcessing", "PreValueUse", false); - ReadParameter(param, "PostProcessing", "PreValueAgeStartup", true); - ReadParameter(param, "PostProcessing", "ErrorMessage", false); - ReadParameter(param, "PostProcessing", "CheckDigitIncreaseConsistency", false); + ReadParameter(param, "PostProcessing", "PreValueUse", false); + ReadParameter(param, "PostProcessing", "PreValueAgeStartup", true); + ReadParameter(param, "PostProcessing", "ErrorMessage", false); + ReadParameter(param, "PostProcessing", "CheckDigitIncreaseConsistency", false); - ReadParameter(param, "MQTT", "Uri", true); - ReadParameter(param, "MQTT", "MainTopic", true); - ReadParameter(param, "MQTT", "ClientID", true); - ReadParameter(param, "MQTT", "user", true); - ReadParameter(param, "MQTT", "password", true); - ReadParameter(param, "MQTT", "RetainMessages", false); - ReadParameter(param, "MQTT", "HomeassistantDiscovery", false); - ReadParameter(param, "MQTT", "MeterType", true); + ReadParameter(param, "MQTT", "Uri", true); + ReadParameter(param, "MQTT", "MainTopic", true); + ReadParameter(param, "MQTT", "ClientID", true); + ReadParameter(param, "MQTT", "user", true); + ReadParameter(param, "MQTT", "password", true); + ReadParameter(param, "MQTT", "RetainMessages", false); + ReadParameter(param, "MQTT", "HomeassistantDiscovery", false); + ReadParameter(param, "MQTT", "MeterType", true); - ReadParameter(param, "InfluxDB", "Uri", true); - ReadParameter(param, "InfluxDB", "Database", true); - ReadParameter(param, "InfluxDB", "Measurement", true); - ReadParameter(param, "InfluxDB", "user", true); - ReadParameter(param, "InfluxDB", "password", true); + ReadParameter(param, "InfluxDB", "Uri", true); + ReadParameter(param, "InfluxDB", "Database", true); + ReadParameter(param, "InfluxDB", "Measurement", true); + ReadParameter(param, "InfluxDB", "user", true); + ReadParameter(param, "InfluxDB", "password", true); - ReadParameter(param, "InfluxDBv2", "Uri", true); - ReadParameter(param, "InfluxDBv2", "Database", true); - ReadParameter(param, "InfluxDBv2", "Measurement", true); - ReadParameter(param, "InfluxDBv2", "Org", true); - ReadParameter(param, "InfluxDBv2", "Token", true); + ReadParameter(param, "InfluxDBv2", "Uri", true); + ReadParameter(param, "InfluxDBv2", "Database", true); + ReadParameter(param, "InfluxDBv2", "Measurement", true); + ReadParameter(param, "InfluxDBv2", "Org", true); + ReadParameter(param, "InfluxDBv2", "Token", true); // ReadParameter(param, "InfluxDB", "Field", true); - ReadParameter(param, "GPIO", "IO0", true); - ReadParameter(param, "GPIO", "IO1", true); - ReadParameter(param, "GPIO", "IO3", true); - ReadParameter(param, "GPIO", "IO4", true); - ReadParameter(param, "GPIO", "IO12", true); - ReadParameter(param, "GPIO", "IO13", true); - ReadParameter(param, "GPIO", "LEDType", false); - ReadParameter(param, "GPIO", "LEDNumbers", false); - ReadParameter(param, "GPIO", "LEDColor", false); - // Folgende Zeilen sind für Abwärtskompatibität < v9.0.0 notwendig (manchmal parameter auskommentiert) - param["GPIO"]["LEDType"]["enabled"] = true; - param["GPIO"]["LEDNumbers"]["enabled"] = true; - param["GPIO"]["LEDColor"]["enabled"] = true; - param["GPIO"]["LEDType"]["found"] = true; - param["GPIO"]["LEDNumbers"]["found"] = true; - param["GPIO"]["LEDColor"]["found"] = true; + ReadParameter(param, "GPIO", "IO0", true); + ReadParameter(param, "GPIO", "IO1", true); + ReadParameter(param, "GPIO", "IO3", true); + ReadParameter(param, "GPIO", "IO4", true); + ReadParameter(param, "GPIO", "IO12", true); + ReadParameter(param, "GPIO", "IO13", true); + ReadParameter(param, "GPIO", "LEDType", false); + ReadParameter(param, "GPIO", "LEDNumbers", false); + ReadParameter(param, "GPIO", "LEDColor", false); + // Folgende Zeilen sind für Abwärtskompatibität < v9.0.0 notwendig (manchmal parameter auskommentiert) + param["GPIO"]["LEDType"]["enabled"] = true; + param["GPIO"]["LEDNumbers"]["enabled"] = true; + param["GPIO"]["LEDColor"]["enabled"] = true; + param["GPIO"]["LEDType"]["found"] = true; + param["GPIO"]["LEDNumbers"]["found"] = true; + param["GPIO"]["LEDColor"]["found"] = true; - ReadParameter(param, "AutoTimer", "AutoStart", false); - ReadParameter(param, "AutoTimer", "Interval", false); - - ReadParameter(param, "DataLogging", "DataLogActive", false); - ReadParameter(param, "DataLogging", "DataFilesRetention", false); + ReadParameter(param, "AutoTimer", "AutoStart", false); + ReadParameter(param, "AutoTimer", "Interval", false); + + ReadParameter(param, "DataLogging", "DataLogActive", false); + ReadParameter(param, "DataLogging", "DataFilesRetention", false); - ReadParameter(param, "Debug", "LogLevel", false); - ReadParameter(param, "Debug", "LogfilesRetention", false); + ReadParameter(param, "Debug", "LogLevel", false); + ReadParameter(param, "Debug", "LogfilesRetention", false); - ReadParameter(param, "System", "TimeZone", true); - ReadParameter(param, "System", "Hostname", true); - ReadParameter(param, "System", "TimeServer", true); - ReadParameter(param, "System", "RSSIThreshold", true); - ReadParameter(param, "System", "CPUFrequency", true); + ReadParameter(param, "System", "TimeZone", true); + ReadParameter(param, "System", "Hostname", true); + ReadParameter(param, "System", "TimeServer", true); + ReadParameter(param, "System", "RSSIThreshold", true); + ReadParameter(param, "System", "CPUFrequency", true); - var sel = document.getElementById("Numbers_value1"); - UpdateInputIndividual(sel); - + var sel = document.getElementById("Numbers_value1"); + UpdateInputIndividual(sel); + // FormatDecimalValue(param, "PostProcessing", "MaxRateValue"); } function FormatDecimalValue(_param, _cat, _name) { - for (var j = 1; j <= _param[_cat][_name]["anzParam"]; ++j) { - var _val = _param[_cat][_name]["value"+j]; - _val = _val.replace(",", "."); - _param[_cat][_name]["value"+j] = _val; - } + for (var j = 1; j <= _param[_cat][_name]["anzParam"]; ++j) { + var _val = _param[_cat][_name]["value"+j]; + _val = _val.replace(",", "."); + _param[_cat][_name]["value"+j] = _val; + } } + function UpdateAfterCategoryCheck() { - ReadParameterAll(); - category["Analog"]["enabled"] = document.getElementById("Category_Analog_enabled").checked; - category["Digits"]["enabled"] = document.getElementById("Category_Digits_enabled").checked; - category["MQTT"]["enabled"] = document.getElementById("Category_MQTT_enabled").checked; - category["InfluxDB"]["enabled"] = document.getElementById("Category_InfluxDB_enabled").checked; - category["InfluxDBv2"]["enabled"] = document.getElementById("Category_InfluxDBv2_enabled").checked; - category["GPIO"]["enabled"] = document.getElementById("Category_GPIO_enabled").checked; + ReadParameterAll(); + category["Analog"]["enabled"] = document.getElementById("Category_Analog_enabled").checked; + category["Digits"]["enabled"] = document.getElementById("Category_Digits_enabled").checked; + category["MQTT"]["enabled"] = document.getElementById("Category_MQTT_enabled").checked; + category["InfluxDB"]["enabled"] = document.getElementById("Category_InfluxDB_enabled").checked; + category["InfluxDBv2"]["enabled"] = document.getElementById("Category_InfluxDBv2_enabled").checked; + category["GPIO"]["enabled"] = document.getElementById("Category_GPIO_enabled").checked; - UpdateInput(); - var sel = document.getElementById("Numbers_value1"); - UpdateInputIndividual(sel); + UpdateInput(); + var sel = document.getElementById("Numbers_value1"); + UpdateInputIndividual(sel); } + function UpdateExpertModus() { // var _style = 'display:none;'; - var _style_pur = 'none'; - var _hidden = true; - if (document.getElementById("ExpertModus_enabled").checked) { + var _style_pur = 'none'; + var _hidden = true; + if (document.getElementById("ExpertModus_enabled").checked) { // _style = ''; - _style_pur = ''; - _hidden = false; - document.getElementById("Edit_Config_Direct").style.display = ""; - } - else - { - document.getElementById("Edit_Config_Direct").style.display = "none"; - } + _style_pur = ''; + _hidden = false; + document.getElementById("Edit_Config_Direct").style.display = ""; + firework.launch("Expert view activated. Please use carefully", 'warning', 5000); + } + else + { + document.getElementById("Edit_Config_Direct").style.display = "none"; + } - const expert = document.querySelectorAll(".expert"); - for (var i = 0; i < expert.length; i++) { - expert[i].style.display = _style_pur; + const expert = document.querySelectorAll(".expert"); + for (var i = 0; i < expert.length; i++) { + expert[i].style.display = _style_pur; // document.getElementById(expert[i].id).style = _style; - } + } + // Enable / Disable die Optionen in den Menues für die Auswahl. Falls kein Expertenmodus soll nur ein Wert (built-in-led oder externan-flash-ws281x) möglich sein + Array.from(document.querySelector("#GPIO_IO4_value1").options).forEach(function(option_element) { + if (option_element.value != "built-in-led") + option_element.hidden = _hidden; + }); - // Enable / Disable die Optionen in den Menues für die Auswahl. Falls kein Expertenmodus soll nur ein Wert (built-in-led oder externan-flash-ws281x) möglich sein - Array.from(document.querySelector("#GPIO_IO4_value1").options).forEach(function(option_element) { - if (option_element.value != "built-in-led") - option_element.hidden = _hidden; - }); - - Array.from(document.querySelector("#GPIO_IO12_value1").options).forEach(function(option_element) { - if (option_element.value != "external-flash-ws281x") - option_element.hidden = _hidden; - }); - + Array.from(document.querySelector("#GPIO_IO12_value1").options).forEach(function(option_element) { + if (option_element.value != "external-flash-ws281x") + option_element.hidden = _hidden; + }); } + function saveTextAsFile() { - ReadParameterAll(); - if (document.getElementsByClassName("invalid-input").length > 0) { - firework.launch("Settings cannot be saved. Please check your entries!", 'danger', 30000); - return; - } + ReadParameterAll(); + if (document.getElementsByClassName("invalid-input").length > 0) { + firework.launch("Settings cannot be saved. Please check your entries!", 'danger', 30000); + return; + } - if (confirm("Are you sure you want to update \"config.ini\"?")) { - ReadParameterAll(); - WriteConfigININew(); - SaveConfigToServer(domainname); - firework.launch('Configuration got updated. It will get applied after the next reboot!', 'success', 5000); - } + if (confirm("Are you sure you want to save the configuration?")) { + ReadParameterAll(); + WriteConfigININew(); + SaveConfigToServer(domainname); + + firework.launch('Configuration saved. It will get applied after the next reboot!', 'success', 5000); + } } + function doReboot() { - if (confirm("Are you sure you want to reboot the ESP32?")) { - var stringota = getDomainname() + "/reboot"; - window.location = stringota; - window.location.href = stringota; - window.location.assign(stringota); - window.location.replace(stringota); - } + if (confirm("Are you sure you want to reboot?")) { + var stringota = getDomainname() + "/reboot"; + window.location = stringota; + window.location.href = stringota; + window.location.assign(stringota); + window.location.replace(stringota); + } } + function editConfigDirect() { - if (confirm("Did you save your changes?")) { - var stringota = getDomainname() + "/edit_config.html?v=$COMMIT_HASH"; - window.location = stringota; - window.location.href = stringota; - window.location.assign(stringota); - window.location.replace(stringota); - } + if (confirm("Proceed to switch the view? Unsaved changes get lost")) { + var stringota = getDomainname() + "/edit_config.html?v=$COMMIT_HASH"; + window.location = stringota; + window.location.href = stringota; + window.location.assign(stringota); + window.location.replace(stringota); + } } + function numberChanged() { - var sel = document.getElementById("Numbers_value1"); - _neu = sel.selectedIndex; - UpdateInputIndividual(sel); + var sel = document.getElementById("Numbers_value1"); + _neu = sel.selectedIndex; + UpdateInputIndividual(sel); - var _selInflux = document.getElementById("NumbersInfluxDBv2_value1"); - if (_selInflux.selectedIndex != _neu) - _selInflux.selectedIndex = _neu + var _selInflux = document.getElementById("NumbersInfluxDBv2_value1"); + if (_selInflux.selectedIndex != _neu) + _selInflux.selectedIndex = _neu } - + + function numberInfluxDBv2Changed() { - var sel = document.getElementById("NumbersInfluxDBv2_value1"); - _neu = sel.selectedIndex; - UpdateInputIndividual(sel); + var sel = document.getElementById("NumbersInfluxDBv2_value1"); + _neu = sel.selectedIndex; + UpdateInputIndividual(sel); - var _sel2 = document.getElementById("Numbers_value1"); - if (_sel2.selectedIndex != _neu) - _sel2.selectedIndex = _neu + var _sel2 = document.getElementById("Numbers_value1"); + if (_sel2.selectedIndex != _neu) + _sel2.selectedIndex = _neu - var _sel3 = document.getElementById("NumbersInfluxDB_value1"); - if (_sel3.selectedIndex != _neu) - _sel3.selectedIndex = _neu + var _sel3 = document.getElementById("NumbersInfluxDB_value1"); + if (_sel3.selectedIndex != _neu) + _sel3.selectedIndex = _neu } + function numberInfluxDBChanged() { - var sel = document.getElementById("NumbersInfluxDB_value1"); - _neu = sel.selectedIndex; - UpdateInputIndividual(sel); + var sel = document.getElementById("NumbersInfluxDB_value1"); + _neu = sel.selectedIndex; + UpdateInputIndividual(sel); + + var _sel2 = document.getElementById("Numbers_value1"); + if (_sel2.selectedIndex != _neu) + _sel2.selectedIndex = _neu - var _sel2 = document.getElementById("Numbers_value1"); - if (_sel2.selectedIndex != _neu) - _sel2.selectedIndex = _neu - - var _sel3 = document.getElementById("NumbersInfluxDBv2_value1"); - if (_sel3.selectedIndex != _neu) - _sel3.selectedIndex = _neu + var _sel3 = document.getElementById("NumbersInfluxDBv2_value1"); + if (_sel3.selectedIndex != _neu) + _sel3.selectedIndex = _neu } + /* hash #description open the details part of the page */ +function openDescription() +{ + if(window.location.hash) { + var hash = window.location.hash.substring(1); //Puts hash in variable, and removes the # character + if(hash == 'description') + document.getElementById("desc_details").open = true; + document.getElementById("reboot").style.display = "none"; + document.getElementById("reboot_text").style.display = "none"; + } +} + +openDescription(); LoadConfigNeu(); diff --git a/sd-card/html/edit_digits.html b/sd-card/html/edit_digits.html index 12b70356..05beee72 100644 --- a/sd-card/html/edit_digits.html +++ b/sd-card/html/edit_digits.html @@ -1,137 +1,212 @@ - + - - -Digit ROI's + + Digit ROI - + + + + -th, td { - padding: 5px 5px 5px 0px; -} - - - - -

Digit ROI's

-

On this page you define ROI's for the digits. - See https://jomjol.github.io/AI-on-the-edge-device-docs/ROI-Configuration/ for explanations.

- -

+

Digit ROI

+
+ CLICK HERE for usage description. More infos in documentation: + ROI Configuration + +

+ Region Of Interest (ROI) for digit numbers can be defined on this page. If no digit numbers need to be processed, + disable digit processing by deselecting "Digit ROI Processing". +

+

+ By default one number sequence (a number seqence contains of 1-x digit ROIs + 1-x analog counter ROIs which are processed together) is + predefined and already selected in the drop down "Number sequence". If you need more than one number sequence additional + one's can be added with the buttons next to the drop down. Each number sequence will be processed separately. +

+

+ Using drag and drop by mouse of by manually entering the parameters into the given fields the digit ROIs can be positined to the digit numbers + on the reference image. To have proper ROI definition please check the documentation: + ROI Configuration. It's very important to be + really precise to have reliable processing. With the drop down "ROI" you can change between the different ROIs in the selected + number sequence. To create new ROIs use "New ROI". +

+

+ The order of the ROIs defines the position (and therefore the multiplication factor) within the reading sequence. The position + in the number sequence can be changed with the buttons "Move ROI Lower" and "Move ROI Higher". The multiplication factor which is + shown below the ROI drop down is the multiplication factor of pure position/order in number sequence and the factor right-hand side to this is + the additionally corrected by decimal shift setting (configuration, expert parameter, default: 0). +

+

+ After definition of digit ROIs is completed don't forget to save with the "Save Config" button!
+ NOTE: There is no need to perform a reboot after every saving or step. It's sufficient to reboot after all configuration steps + (reference image, alignment, ROI configuration) are completed to activate new configuration. +

+
+
+ +

+ + +

-

After saving the digit ROI's, you can define the analog ROI's if your meter has analog counters.
- Only after those steps a reboot is required.

-
- - - - -
- -

- - - Number: +
+ + + + + + + + + + + -
Number Sequence:
- - - -
-

- + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + +
ROI:
Multiplier:
+ (only based on order) +
Multiplier:
+ (order + decimal shift: ) +
+ - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + +
- - - - - - -
x: Δx:
y: Δy:
-
x: Δx:
y: Δy:
+ +
-
- + - - - -
-

Proceed to update the analog ROI's when you are done or reboot if there are no analogue counters.

+ + + + + + + + Reference Image: + + + + + + @@ -183,6 +258,7 @@ function EnDisableDigits() { sah1(document.getElementById("div1"), !isEnabled); cofcat["Digits"]["enabled"] = isEnabled; + document.getElementById("saveroi").disabled = false; if (isEnabled) { @@ -208,6 +284,10 @@ function onNameChange(){ } function deleteROI(){ + if (!confirm("Delete the selected ROI?")) { + return; //break out of the function early because prompt was aborted + } + ROIInfo.splice(aktindex, 1); if (aktindex > ROIInfo.length - 1){ aktindex = ROIInfo.length - 1; @@ -222,7 +302,10 @@ function newROI() { sel = document.getElementById("index"); var _roialt= sel.options[sel.selectedIndex].text; - var _roinew = prompt("Please enter name of new ROI", "name"); + var _roinew = prompt("Please enter a name for the new ROI", "name"); + if (_roinew === null) { + return; //break out of the function early because prompt was aborted + } if (ROIInfo.length > 0) { if (ROIInfo.length > 1) { @@ -232,7 +315,7 @@ function newROI() { parseInt(ROIInfo[sel.selectedIndex].y), ROIInfo[aktindex]["dx"], ROIInfo[aktindex]["dy"], 0); } else - erg = CreateROI(_number, "digit", sel.selectedIndex, _roinew, 1, 1, 30, 51, 0); + erg = CreateROI(_number, "digit", sel.selectedIndex, _roinew, 15, 30, 30, 51, 0); if (erg != "") firework.launch(erg, 'danger', 30000); @@ -274,7 +357,7 @@ function changelockSizes(){ valuemanualchangedspace(); if (!lockSizes) { - firework.launch("For best results it is in most cases advised to keep the y, Δx and Δy identical!", 'warning', 10000); + firework.launch("In most cases it's advised to keep the y, Δx and Δy identical!", 'warning', 10000); } } @@ -296,11 +379,56 @@ function ChangeSelection(){ } function SaveToConfig(){ -// _zwcat = getConfigCategory(); - cofcat["Digits"]["enabled"] = document.getElementById("Category_Digits_enabled").checked; - WriteConfigININew(); - SaveConfigToServer(domainname); - firework.launch('Configuration got updated. It will get applied after the next reboot!', 'success', 5000); + if (confirm("Are you sure you want to save the new digit ROI configuration?")) { + // _zwcat = getConfigCategory(); + cofcat["Digits"]["enabled"] = document.getElementById("Category_Digits_enabled").checked; + WriteConfigININew(); + SaveConfigToServer(domainname); + UpdateROIs(); + document.getElementById("saveroi").disabled = true; + + firework.launch('Configuration saved. It will get applied after next reboot', 'success', 5000); + } +} + + +function ShowMultiplier(){ + var decimalShift = 0; + var negShift = false; + var multiplier = multiplier_decshift = ROIInfo.length-1-aktindex; + var fixedDecimals_decshift = ROIInfo.length-1-aktindex; + var NumberInfo = getNUMBERInfo(); + + var sel = document.getElementById("Numbers_value1"); + var _number= sel.options[sel.selectedIndex].text; + var NumbersIndex = 0; + for (var i = 0; i < NumberInfo.length; ++i) + if (NumberInfo[i]["name"] == _number) + NumbersIndex = i; + + if (NumberInfo[NumbersIndex]["PostProcessing"]["DecimalShift"]["enabled"]) { + decimalShift = NumberInfo[NumbersIndex]["PostProcessing"]["DecimalShift"]["value1"]; + document.getElementById("decimalShift").value=decimalShift; + multiplier_decshift = multiplier_decshift+Number(decimalShift); + fixedDecimals_decshift = fixedDecimals_decshift+Number(decimalShift); // set to fixed decimals to avoid rounding issues + + if (fixedDecimals_decshift < 0) { + negShift = true + fixedDecimals_decshift = -1*fixedDecimals_decshift; + } + } + else { + document.getElementById("decimalShift").value=0; + } + + if (!negShift) { + document.getElementById("multiplier").value="x" + Number(10 ** multiplier).toFixed(0); + document.getElementById("multiplier_decshift").value="x" + Number(10 ** multiplier_decshift).toFixed(0); + } + else { + document.getElementById("multiplier").value="x" + Number(10 ** multiplier).toFixed(0); + document.getElementById("multiplier_decshift").value="x" + Number(10 ** multiplier_decshift).toFixed(fixedDecimals_decshift); + } } @@ -316,19 +444,28 @@ function UpdateROIs(_sel){ { document.getElementById("Category_Digits_enabled").checked = false; EnDisableDigits(); - firework.launch('Digital ROIs are disabled - please enable first (Check box top left)', 'warning', 10000); + firework.launch('Digit ROI processing is disabled. Activate with checkbox if needed', 'warning', 10000); return; } if (ROIInfo.length == 0){ - firework.launch('There are no ROIs defined. Please first create a new ROI ("New ROIs ...")', 'danger', 10000); + firework.launch('No digit ROIs defined in selected number sequence', 'warning', 10000); document.getElementById("newROI").disabled = false; document.getElementById("deleteROI").disabled = true; - document.getElementById("index").disabled = true; - document.getElementById("saveroi").disabled = true; document.getElementById("renameROI").disabled = true; + document.getElementById("index").disabled = true; + document.getElementById("multiplier").style.display = "none"; + document.getElementById("multiplier_decshift").style.display = "none"; + document.getElementById("refx").disabled = true; + document.getElementById("refdx").disabled = true; + document.getElementById("refy").disabled = true; + document.getElementById("refdy").disabled = true; + document.getElementById("lockSizes").disabled = true; + document.getElementById("lockAspectRatio").disabled = true; + document.getElementById("lockSpaceEquidistant").disabled = true; document.getElementById("moveNext").disabled = true; document.getElementById("movePrevious").disabled = true; + document.getElementById("saveroi").disabled = false; return; } else @@ -337,6 +474,15 @@ function UpdateROIs(_sel){ document.getElementById("deleteROI").disabled = false; document.getElementById("renameROI").disabled = false; document.getElementById("index").disabled = false; + document.getElementById("multiplier").style.display = ""; + document.getElementById("multiplier_decshift").style.display = ""; + document.getElementById("refx").disabled = false; + document.getElementById("refdx").disabled = false; + document.getElementById("refy").disabled = false; + document.getElementById("refdy").disabled = false; + document.getElementById("lockSizes").disabled = false; + document.getElementById("lockAspectRatio").disabled = false; + document.getElementById("lockSpaceEquidistant").disabled = false; document.getElementById("saveroi").disabled = false; } @@ -346,7 +492,7 @@ function UpdateROIs(_sel){ } if (aktindex > ROIInfo.length) - aktindex = ROIInfo.length; + aktindex = ROIInfo.length-1; for (var i = 0; i < ROIInfo.length; ++i){ var option = document.createElement("option"); @@ -369,7 +515,9 @@ function UpdateROIs(_sel){ document.getElementById("moveNext").disabled = false; if (aktindex == (ROIInfo.length-1)){ document.getElementById("moveNext").disabled = true; - } + } + + ShowMultiplier(); document.getElementById("lockAspectRatio").checked = lockAspectRatio; document.getElementById("lockSizes").checked = lockSizes; @@ -390,7 +538,7 @@ function UpdateROIs(_sel){ rect.startY = ROIInfo[aktindex]["y"]; rect.w = ROIInfo[aktindex]["dx"]; rect.h = ROIInfo[aktindex]["dy"]; - draw(); + draw(); } function loadCanvas(dataURL) { @@ -420,8 +568,20 @@ function UpdateROIs(_sel){ var left = box.left + scrollLeft - clientLeft; return { top: Math.round(top), left: Math.round(left) }; } + + + /* hash #description open the details part of the page */ + function openDescription() { + if(window.location.hash) { + var hash = window.location.hash.substring(1); //Puts hash in variable, and removes the # character + if(hash == 'description') + document.getElementById("desc_details").open = true; + } + } + function init() { + openDescription(); domainname = getDomainname(); canvas.addEventListener('mousedown', mouseDown, false); canvas.addEventListener('mouseup', mouseUp, false); @@ -481,6 +641,8 @@ function UpdateROIs(_sel){ document.getElementById("space").value = space; } + document.getElementById("saveroi").disabled = true; + drawImage(); draw(); } @@ -523,10 +685,13 @@ function UpdateNUMBERS(_sel){ function renameNumber(){ var sel = document.getElementById("Numbers_value1"); - var _delte= sel.options[sel.selectedIndex].text; - var _numbernew = prompt("Please enter new name", _delte); + var _delete= sel.options[sel.selectedIndex].text; + var _numbernew = prompt("Please enter a new name for the selected number sequence", _delete); + if (_numbernew === null) { + return; //break out of the function early because prompt was aborted + } - erg = RenameNUMBER(_delte, _numbernew); + erg = RenameNUMBER(_delete, _numbernew); if (erg != "") firework.launch(erg, 'danger', 30000); else @@ -534,21 +699,27 @@ function renameNumber(){ } function newNumber(){ - var _numbernew = prompt("Please enter name of new number", "name"); + var _numbernew = prompt("Please enter a name for the new number sequence", "name"); + if (_numbernew === null) { + return; //break out of the function early because prompt was aborted + } erg = CreateNUMBER(_numbernew); if (erg != "") - firework.launch(erg, 'danger', 30000); + firework.launch(erg, 'danger', 30000); else UpdateNUMBERS(_numbernew); } function removeNumber(){ - if (confirm("This will remove the number complete (analog and digital).\nIf you only want to remove the digital ROIs, please use \"Delete ROIs\".\nDo you want to proceed?")) { + if (confirm("The entire number sequence will be removed (digit + analog parts). " + + "To remove single ROI of the number sequence, use \"Delete ROI\" instead.\n" + + "Do you really want to proceed?")) + { var sel = document.getElementById("Numbers_value1"); - var _delte= sel.options[sel.selectedIndex].text; - erg = DeleteNUMBER(_delte); + var _delete= sel.options[sel.selectedIndex].text; + erg = DeleteNUMBER(_delete); if (erg != "") firework.launch(erg, 'danger', 30000); UpdateNUMBERS(); @@ -755,6 +926,7 @@ function draw() { } draw(); } + document.getElementById("saveroi").disabled = false; } function valuemanualchangeddx(){ @@ -780,6 +952,7 @@ function draw() { draw(); } + document.getElementById("saveroi").disabled = false; } function valuemanualchangedspace(){ @@ -821,8 +994,10 @@ function draw() { sel = document.getElementById("index"); var _roialt= sel.options[sel.selectedIndex].text; - - var _roinew = prompt("Please enter new name", _roialt); + var _roinew = prompt("Please enter a new name for the selected ROI", _roialt); + if (_roinew === null) { + return; //break out of the function early because prompt was aborted + } erg = RenameROI(_number, "digit", _roialt, _roinew); if (erg != "") @@ -833,6 +1008,7 @@ function draw() { function numberChanged() { + aktindex = 0; UpdateROIs(); } diff --git a/sd-card/html/edit_explain_0.html b/sd-card/html/edit_explain_0.html index 64aea8b1..c2f12129 100644 --- a/sd-card/html/edit_explain_0.html +++ b/sd-card/html/edit_explain_0.html @@ -1,10 +1,8 @@ - + - AI on the edge - - + + + + + + + + +

Step 7 / 7: Initial setup completed!

+ +

+ Congratulations! You have completed the initial setup and you are now ready to restart to the regular operation mode! +

+

+ Once you push the button below, the setup mode will be completed and the device will be automatically restart to regular web interface. + If configuration is error free, the device will automatically start with first processing. It will take some time until you get the first reading. +

+

+ +

+ + + + + + + + + diff --git a/sd-card/html/edit_explain_6.html b/sd-card/html/edit_explain_7_abort.html similarity index 50% rename from sd-card/html/edit_explain_6.html rename to sd-card/html/edit_explain_7_abort.html index 9af52302..3e66fd1e 100644 --- a/sd-card/html/edit_explain_6.html +++ b/sd-card/html/edit_explain_7_abort.html @@ -1,10 +1,8 @@ - + - AI on the edge - - + - - - + input:invalid { + background-color: rgba(255, 0, 0, 0.25); + border: 1px solid red; + } + + .button { + padding: 5px 10px; + width: 205px; + font-size: 16px; + } + + th, td { + padding: 5px 5px 5px 0px; + } + + table { + width: 660px; + padding: 5px; + } + + + + +

Reference Image

-

On this page you define the Reference Image. - See https://jomjol.github.io/AI-on-the-edge-device-docs/Reference-Image/ for explanations.

-

After saving a new Reference Image, make sure to update the Alignment Marks and then - the digit resp. analog ROI's.
- Only after those steps a reboot is required.

+
+ CLICK HERE for usage description. More infos in documentation: + Reference Image + +

+ The reference image is the base image on which the alignment markers, digit ROIs and anlog ROIs will be defined. +

+

+ Firstly the actual saved reference image is shown. If you start with the setup from scratch a default image is shown as placeholder. + Use the button "Create New Reference" to start creation of your own reference image. After selecting the button all configured parameter + will be applied to the shown image. With the button "Update Image" you can update the image (still all parameter + get applied to the new image). +

+

+ To have reliable evaluation processing a properly horizontal aligned evaluation area is mandatory. Using the parameters "Rotation angle" and + "Rotation angle (Fine-tune)" the image can be rotated in both directions. The resulting rotation anlge is used to prerotate the image before + the alignment algorithm is processed to compensate only small misalignments. Further information can be found in documenation: + Reference Image +

+

+ After setting up your reference image don't forget to save with the "Save New Reference" button!
+ NOTE: There is no need to perform a reboot after every saving or step. It's sufficient to reboot after all configuration steps + (reference image, alignment, ROI configuration) are completed to activate new configuration. +

+
+
- - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- LEDIntensity: - -
- Brightness: - -
Degrees - Contrast - -
Degrees - Saturation - -
- - - - - - - - -
-

Proceed to update the Alignment Marks when you are done.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ LED intensity: + + +
+ Brightness: + + + 0 +
+ degree + + Contrast: + + + 0 +
+ degree + + Saturation: + + + 0 +
+ + + + + + + + + + + + + + +
Reference Image: + +
@@ -102,7 +177,6 @@ table { - diff --git a/sd-card/html/explain_1.html b/sd-card/html/explain_1.html index 0a15e9db..da6785b0 100644 --- a/sd-card/html/explain_1.html +++ b/sd-card/html/explain_1.html @@ -1,34 +1,33 @@ - + + - AI on the edge - - + -

Step 1: Define a Reference Image

-The reference image is the base to define the digits, counters and references positions. -

- Firstly you see the default image. Use the Button "Create New Reference" to start to create your own reference image.
- Most important feature is a straight alignment of the image. Use the Pre-rotate angle and the fine alignment to adjust the rotation of the image. -

-

- Don't forget to save your changes with the "Save" button! -

+

Step 1 / 7: Adjust Focus And Verify Flashlight

+ Firstly you have find a proper mounting position and potentially have to adjust the focus of the camera lens to get a sharp and crisp image. + This live stream with flashlight on could be helpful for this task. More details about adjusting the camera lens can be found here: + Focus Adjustment
+ Additionally it should be verfied that the flashlight is not creating any distrubing reflection in the processing relevant areas. + Beside using the built-in internal flash LED it's also possible to attach additional external LEDs to the device to have more possiblities + to get proper light condition. Please read the documentation if you'd like to use extenal LEDs: + Installation Of External LEDs +

+ NOTE: The flashlight indensity is set to default (50%) for initial verfication in this step and can be modified in next step. After modification + you can come back to this step if you'd like to test with adjusted light intensity.
+ The live stream can also be called at any time also after setup mode is completed on regular web interface. +

diff --git a/sd-card/html/explain_2.html b/sd-card/html/explain_2.html index 76812db6..8187889f 100644 --- a/sd-card/html/explain_2.html +++ b/sd-card/html/explain_2.html @@ -1,34 +1,21 @@ - + + - AI on the edge - - + -

Step 2: Define two Alignment Marks

-Two opposite alignment marks are needed to identify unique fix points on the image. -

- Mark the reference by drag and dop with the mouse or with the coordinates and push "Update Reference". -
- You can switch between the two reference with "Select Reference". -

-

- Don't forget to save your changes with the "Save" button! -

+

Step 2 / 7: Create A Reference Image

diff --git a/sd-card/html/explain_3.html b/sd-card/html/explain_3.html index 8742a8ac..860cae34 100644 --- a/sd-card/html/explain_3.html +++ b/sd-card/html/explain_3.html @@ -1,36 +1,21 @@ - + + - AI on the edge - - + -

Step 3: Define ROI's for your Digits

-Here you define your digits you want to read. If you have more than one number on the reading you can define several numbers with the "Number" selector. There you can also define new numbers. -

- With the drop down menue "ROI x" you can change between the different digits. Mark them with the mouse or the coordinates. -
- To create new ROIs use "New ROIs". The order of the ROIs defines the positon within the reading.
- You can change it with "move Next" / "move Previous". -

-

- Don't forget to save your changes with the "Save" button! -

+

Step 3 / 7: Definement Of Alignment Marker

diff --git a/sd-card/html/explain_4.html b/sd-card/html/explain_4.html index 0fbb9e54..1df0c15a 100644 --- a/sd-card/html/explain_4.html +++ b/sd-card/html/explain_4.html @@ -1,35 +1,21 @@ - + + - AI on the edge - - + -

Step 4: Define ROI's for your Analog Counters

-Here you define your analog counters you want to read. If you have more than one number on the reading you can define several numbers with the "Number" selector. There you can also define new numbers. If you do not have analog counters delete all ROIs. -

- With the drop down menue "ROI x" you can change between the different counters. Mark them with the mouse or the coordinates. -
- To create new ROIs use "New ROIs". The order of the ROIs defines the positon within the reading.
- You can change it with "move Next" / "move Previous". -

-

- Don't forget to save your changes with the "Save" button! -

+

Step 4 / 7: Configuration Of ROIs For Digit Numbers

diff --git a/sd-card/html/explain_5.html b/sd-card/html/explain_5.html index 4c1ddd63..2610fa46 100644 --- a/sd-card/html/explain_5.html +++ b/sd-card/html/explain_5.html @@ -1,31 +1,21 @@ - - - -AI on the edge - - + - -

Step 5: General Configuration Settings

-

Here you can define additional settings. The default settings should fit for a normal/initial setup.

-

You will also be able to change them later, so don't worry if you do not understand the parameters yet!

-

-

- Don't forget to save your changes with the "Save" button! -

+

Step 5 / 7: Configuration Of ROIs For Analog Counters

- + \ No newline at end of file diff --git a/sd-card/html/explain_6.html b/sd-card/html/explain_6.html index 7fbc484a..29020f5e 100644 --- a/sd-card/html/explain_6.html +++ b/sd-card/html/explain_6.html @@ -1 +1,23 @@ - \ No newline at end of file + + + + +AI on the edge + + + + + + + +

Step 6 / 7: Configuration Page: List Of All Parameters

+

+ NOTE: All parameter can be edited later in the regular web interface. So don't worry if you do not understand the parameters yet! +

+ + diff --git a/sd-card/html/explain_7.html b/sd-card/html/explain_7.html new file mode 100644 index 00000000..7fbc484a --- /dev/null +++ b/sd-card/html/explain_7.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/sd-card/html/file_server.html b/sd-card/html/file_server.html index 51cf37c5..59189266 100644 --- a/sd-card/html/file_server.html +++ b/sd-card/html/file_server.html @@ -1,9 +1,15 @@ - + + - - - - - - - -
-

Content on SD-Card

-
- + + +
- - + - - -
- + +

Fileserver

- + + + + + + + + + + + + +
+ + + +
+ + + + + +
- - - - - + + +
-
-      -
- - + + checkAtRootLevel(); - + console.log("Current path: " + getPath().replace("/fileserver", "")); + document.getElementById("currentpath").innerHTML = "Current path: " + getPath().replace("/fileserver", "") + ""; + + document.cookie = "page=" + getPath() + "; path=/"; + + + \ No newline at end of file diff --git a/sd-card/html/firework.css b/sd-card/html/firework.css index e905579e..db3cd49c 100644 --- a/sd-card/html/firework.css +++ b/sd-card/html/firework.css @@ -4,13 +4,15 @@ border-color: #888; border-radius: 6px; color: #fff; - left: 200px; + left: 10%; padding: 5px; position: fixed; opacity: 0; text-align: center; top: 0px; - width: 600px; + width: 80%; + min-width:30%; + max-width:580px; z-index: 99; font-size:120%; } diff --git a/sd-card/html/firework.js b/sd-card/html/firework.js index 19bbfc56..46306a10 100644 --- a/sd-card/html/firework.js +++ b/sd-card/html/firework.js @@ -29,7 +29,7 @@ p += parseInt($(this).height()) + 30 }); - $('
'+ m +'
') + $('
'+ m +'
') .appendTo('body') .animate({ opacity: 1, diff --git a/sd-card/html/graph.html b/sd-card/html/graph.html index 74843d55..fc46aeb9 100644 --- a/sd-card/html/graph.html +++ b/sd-card/html/graph.html @@ -1,16 +1,40 @@ - - + + + + Data Graph - + + - + - - -

Data Graph

-

Loading...

- - - - -   |   - - + - + +

Data Graph

+

Loading...

+ Number sequence: + + Day: + +

+ + + diff --git a/sd-card/html/index.html b/sd-card/html/index.html index b624afff..d4ea6fa1 100644 --- a/sd-card/html/index.html +++ b/sd-card/html/index.html @@ -1,65 +1,76 @@ - + - -AI on the edge - - - - + + AI on the edge + + + + - - - + + + - - - + + - + function resetMenu() { + [].forEach.call(document.querySelectorAll('.submenu'), function (el) { + el.style.visibility = 'visible'; + }); + } + + function getCookie(cname) { + let name = cname + "="; + let decodedCookie = decodeURIComponent(document.cookie); + let ca = decodedCookie.split(';'); + for(let i = 0; i
- - - +

Digitizer - AI on the edge

-

An ESP32 all inclusive neural network recognition system for meter digitalization

-
+ + + +

Digitizer - AI on the edge

+

An ESP32 all inclusive neural network recognition system for meter digitalization

+
- + -Loading version... +Loading version... + } + console.log("Loading page: " + getCookie("page")); + document.getElementById('maincontent').src = getCookie("page"); -
+ + function flow_start() { + var url = getDomainname() + '/flow_start'; + var xhttp = new XMLHttpRequest(); + xhttp.onreadystatechange = function() { + if (this.readyState == 4 && this.status == 200) { + if (xhttp.responseText.substring(0,3) == "001") { + firework.launch('Flow start triggered', 'success', 5000); + window.location.reload(); + } + else if (xhttp.responseText.substring(0,3) == "002") { + firework.launch('Flow start scheduled. Start after round is completed', 'success', 5000); + } + else if (xhttp.responseText.substring(0,3) == "099") { + firework.launch('Flow start triggered, but start not possible (no flow task available)', 'danger', 5000); + } + } + } + xhttp.open("GET", url, true); + xhttp.send(); + } + + + function HA_send_discovery_visiblilty() { + loadConfig(domainname); + ParseConfig(); + category = getConfigCategory(); + param = getConfigParameters(); + if (category["MQTT"]["enabled"] && param["MQTT"]["HomeassistantDiscovery"]["value1"] == "true") + document.getElementById("HASendDiscovery").style.display=""; + } + + + function HA_send_discovery() { + var url = getDomainname() + '/mqtt_publish_discovery'; + var xhttp = new XMLHttpRequest(); + xhttp.onreadystatechange = function() { + if (this.readyState == 4 && this.status == 200) { + firework.launch('Sending HA discovery topics scheduled. The sending will be processed in state "Publish to MQTT"', 'success', 5000); + } + } + xhttp.open("GET", url, true); + xhttp.send(); + } + + + function start_livestream(streamFlashlight) { + if (streamPopup) + streamPopup.close(); + + if (streamFlashlight) + streamPopup = window.open(getDomainname() + '/stream?flashlight=true','LivestreamWithlight',streamWindowFeatures); + else + streamPopup = window.open(getDomainname() + '/stream','Livestream',streamWindowFeatures); + + streamPopup.focus(); + } + diff --git a/sd-card/html/info.html b/sd-card/html/info.html index 08fd57e6..9ab7d94a 100644 --- a/sd-card/html/info.html +++ b/sd-card/html/info.html @@ -1,9 +1,8 @@ - + - Info - + + -

Runtime Information

- - - - - - - - - -
- Last restart: - -
- -
-
- Uptime: - -
- -
-
+ + + + + + +

Runtime Information

+ + + + + + + + + +
Start time:
Uptime:
-

Build Info

- - - - - - - - - +
- Firmware Version: - -
- -
-
- Firmware Build Time: - -
- -
-
+ + + + + +

Build Info

+ + + + + + + + + - - - - -
Firmware version: + +
Firmware build time: + +
- Web Interface Version: - -
- -
-
+ + Web interface version: + + + + + -

Host Info

- - - - - - - - - - - - - -
- Hostname: - -
- -
-
- IP-Address: - -
- -
-
- WLan-SSID: - -
- -
-
+ + + + + + +

Host Info

+ + + + + + + + + + + + + +
Hostname: + +
IP Address: + +
WLAN SSID: + +
+ + + + + + + +

SD Card Info

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
SD card manufacturer: + +
SD card name: + +
SD card size: + +
SD card sector size: + +
Partition size: + +
Partition free space: + +
Partition allocation size: + +
+ + + + + + + +

Memory Info

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Total Free (Int + Ext): + +
Ext. RAM - Free: + +
Ext. RAM - Largest Free Block: + +
Ext. RAM - Min Free: + +
Int. RAM - Free: + +
Int. RAM - Largest Free Block: + +
Int. RAM - Min Free: + +
-

SD Card Info

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- SD Card Manufacturer: - -
- -
-
- SD Card Name: - -
- -
-
- SD Card Size [MB]: - -
- -
-
- SD Card Sector Size [byte]: - -
- -
-
- Partition Size [MB]: - -
- -
-
- Partition Free Space [MB]: - -
- -
-
- Partition Allocation Size [byte]: - -
- -
-

Copyright

Copyright © 2020 - 2023 by Jomjol and others. + + diff --git a/sd-card/html/log.html b/sd-card/html/log.html index cad504f5..503941f4 100644 --- a/sd-card/html/log.html +++ b/sd-card/html/log.html @@ -1,16 +1,19 @@ - + + + Log Viewer +
- - - + + +
-



Loading Logfile, please wait...
+



Loading logfile, please wait...
diff --git a/sd-card/html/ota_page.html b/sd-card/html/ota_page.html index f97bf753..adaf4fa7 100644 --- a/sd-card/html/ota_page.html +++ b/sd-card/html/ota_page.html @@ -1,27 +1,25 @@ - + - OTA Update - + @@ -51,11 +49,11 @@


- +

+

Status: Idle

-

Status: idle

@@ -262,10 +260,10 @@ function progressHandler(event) { _("loaded_n_total").innerHTML = "Uploaded " + (event.loaded / 1024 / 1024).toFixed(2) + - " MBytes of " + (event.total / 1024/ 1024).toFixed(2) + " MBytes."; + " MB of " + (event.total / 1024/ 1024).toFixed(2) + " MB"; var percent = (event.loaded / event.total) * 100; _("progressBar").value = Math.round(percent); - _("status").innerHTML = "Status: " + Math.round(percent) + "% uploaded... please wait"; + _("status").innerHTML = "Status: " + Math.round(percent) + "% uploaded. Please wait..."; } diff --git a/sd-card/html/overview.html b/sd-card/html/overview.html index 4ef17bae..6d22f62c 100644 --- a/sd-card/html/overview.html +++ b/sd-card/html/overview.html @@ -1,77 +1,89 @@ - + - Overview - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Value:
-
-
Previous Value:
-
-
Raw Value:
-
-
Status:
-
-
-
-
-
-
-
- - -
-
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Value + +
+
+
Previous Value
+
+
Raw Value
+
+
Value Status
+
+
Process State
+
+
System Info
+
+
+
+
+ + +
+
- - - - + + + + @@ -104,6 +156,7 @@ function setprevalue() { xhttp.send(); response = xhttp.responseText; document.getElementById("result").innerHTML=response; + firework.launch('New \"previous value\" set', 'success', 5000); } catch (error) { @@ -145,11 +198,10 @@ function loadPrevalue(_domainname) { lines.forEach(function(line) { arr = line.split("\t"); if (_number == arr[0]) { - document.getElementById("myInput").value=arr[1]; + document.getElementById("myInput").value = Number(arr[1]); return; } }); - } catch (error) { @@ -190,7 +242,7 @@ function UpdateNUMBERS(_sel){ function init(){ - domainname = getDomainname(); + domainname = getDomainname(); loadConfig(domainname); ParseConfig(); UpdateNUMBERS(); diff --git a/sd-card/html/readconfigcommon.js b/sd-card/html/readconfigcommon.js index f256df8e..34ca12a1 100644 --- a/sd-card/html/readconfigcommon.js +++ b/sd-card/html/readconfigcommon.js @@ -85,6 +85,7 @@ function ZerlegeZeile(input, delimiter = " =\t\r") } + function findDelimiterPos(input, delimiter) { var pos = -1; @@ -131,7 +132,8 @@ function getConfig() } -function loadConfig(_domainname) { +function loadConfig(_domainname) +{ var xhttp = new XMLHttpRequest(); try { url = _domainname + '/fileserver/config/config.ini'; @@ -148,17 +150,17 @@ function loadConfig(_domainname) { } - - -function dataURLtoBlob(dataurl) { +function dataURLtoBlob(dataurl) +{ var arr = dataurl.split(','), mime = arr[0].match(/:(.*?);/)[1], bstr = atob(arr[1]), n = bstr.length, u8arr = new Uint8Array(n); while(n--){ u8arr[n] = bstr.charCodeAt(n); } return new Blob([u8arr], {type:mime}); - } - +} + + function FileCopyOnServer(_source, _target, _domainname = ""){ url = _domainname + "/editflow?task=copy&in=" + _source + "&out=" + _target; var xhttp = new XMLHttpRequest(); @@ -171,6 +173,7 @@ function FileCopyOnServer(_source, _target, _domainname = ""){ } } + function FileDeleteOnServer(_filename, _domainname = ""){ var xhttp = new XMLHttpRequest(); var okay = false; @@ -201,6 +204,7 @@ function FileDeleteOnServer(_filename, _domainname = ""){ return okay; } + function FileSendContent(_content, _filename, _domainname = ""){ var xhttp = new XMLHttpRequest(); var okay = false; @@ -242,6 +246,7 @@ function SaveCanvasToImage(_canvas, _filename, _delete = true, _domainname = "") FileSendContent(rtn, _filename, _domainname); } + function MakeContrastImageZW(zw, _enhance, _domainname){ _filename = zw["name"].replace("/config/", "/img_tmp/"); url = _domainname + "/editflow?task=cutref&in=/config/reference.jpg&out=" + _filename + "&x=" + zw["x"] + "&y=" + zw["y"] + "&dx=" + zw["dx"] + "&dy=" + zw["dy"]; @@ -260,7 +265,7 @@ function MakeContrastImageZW(zw, _enhance, _domainname){ } if (xhttp.responseText == "CutImage Done") { - firework.launch('Reference Image Contrast got enhanced.', 'success', 5000); + firework.launch('Image Contrast got enhanced', 'success', 5000); return true; } else { @@ -270,7 +275,6 @@ function MakeContrastImageZW(zw, _enhance, _domainname){ } - function MakeRefZW(zw, _domainname){ _filetarget = zw["name"].replace("/config/", "/img_tmp/"); _filetarget = _filetarget.replace(".jpg", "_org.jpg"); @@ -289,7 +293,7 @@ function MakeRefZW(zw, _domainname){ _filetarget2 = zw["name"].replace("/config/", "/img_tmp/"); // _filetarget2 = _filetarget2.replace(".jpg", "_org.jpg"); FileCopyOnServer(_filetarget, _filetarget2, _domainname); - firework.launch('Reference Image got updated.', 'success', 5000); + firework.launch('Marker got updated', 'success', 5000); return true; } else { diff --git a/sd-card/html/readconfigparam.js b/sd-card/html/readconfigparam.js index a2138143..c31c0b4d 100644 --- a/sd-card/html/readconfigparam.js +++ b/sd-card/html/readconfigparam.js @@ -710,9 +710,10 @@ function getNUMBERInfo(){ } function RenameNUMBER(_alt, _neu){ - if ((_neu.indexOf(".") >= 0) || (_neu.indexOf(",") >= 0) || (_neu.indexOf(" ") >= 0) || (_neu.indexOf("\"") >= 0)) + if ((_neu.indexOf(".") >= 0) || (_neu.indexOf(",") >= 0) || + (_neu.indexOf(" ") >= 0) || (_neu.indexOf("\"") >= 0)) { - return "Name must not contain ',', '.', ' ' or '\"' - please change name"; + return "Number sequence name must not contain , . \" or a space"; } index = -1; @@ -725,21 +726,21 @@ function RenameNUMBER(_alt, _neu){ } if (found) - return "Name is already existing - please use another name"; + return "Number sequence name is already existing, please choose another name"; NUMBERS[index]["name"] = _neu; return ""; } -function DeleteNUMBER(_delte){ +function DeleteNUMBER(_delete){ if (NUMBERS.length == 1) - return "The last number cannot be deleted." + return "One number sequence is mandatory. Therefore this cannot be deleted" index = -1; for (i = 0; i < NUMBERS.length; ++i) { - if (NUMBERS[i]["name"] == _delte) + if (NUMBERS[i]["name"] == _delete) index = i; } @@ -758,7 +759,7 @@ function CreateNUMBER(_numbernew){ } if (found) - return "Name does already exist, please choose another one!"; + return "Number sequence name is already existing, please choose another name"; _ret = new Object(); _ret["name"] = _numbernew; @@ -796,24 +797,26 @@ function CreateNUMBER(_numbernew){ function getROIInfo(_typeROI, _number){ - index = 0; + index = -1; for (var i = 0; i < NUMBERS.length; ++i) if (NUMBERS[i]["name"] == _number) index = i; - return NUMBERS[index][_typeROI]; + if (index != -1) + return NUMBERS[index][_typeROI]; + else + return ""; } function RenameROI(_number, _type, _alt, _neu){ if ((_neu.includes("=")) || (_neu.includes(".")) || (_neu.includes(":")) || (_neu.includes(",")) || (_neu.includes(";")) || (_neu.includes(" ")) || - (_neu.includes("\""))) { - return "Name must not contain any of the following characters: . : , ; = \" ' '"; + (_neu.includes("\""))) + { + return "ROI name must not contain . : , ; = \" or space"; } - - index = -1; found = false; _indexnumber = -1; @@ -821,6 +824,9 @@ function RenameROI(_number, _type, _alt, _neu){ if (NUMBERS[j]["name"] == _number) _indexnumber = j; + if (_indexnumber == -1) + return "Number sequence not existing. ROI cannot be renamed" + for (i = 0; i < NUMBERS[_indexnumber][_type].length; ++i) { if (NUMBERS[_indexnumber][_type][i]["name"] == _alt) index = i; @@ -829,18 +835,18 @@ function RenameROI(_number, _type, _alt, _neu){ } if (found) - return "Name is already existing - please use another name"; + return "ROI name is already existing, please choose another name"; NUMBERS[_indexnumber][_type][index]["name"] = _neu; return ""; } + function DeleteNUMBER(_delte){ if (NUMBERS.length == 1) - return "The last number cannot be deleted." + return "The last number cannot be deleted" - index = -1; for (i = 0; i < NUMBERS.length; ++i) { if (NUMBERS[i]["name"] == _delte) @@ -854,21 +860,24 @@ function DeleteNUMBER(_delte){ return ""; } + function CreateROI(_number, _type, _pos, _roinew, _x, _y, _dx, _dy, _CCW){ _indexnumber = -1; for (j = 0; j < NUMBERS.length; ++j) if (NUMBERS[j]["name"] == _number) _indexnumber = j; + if (_indexnumber == -1) + return "Number sequence not existing. ROI cannot be created" found = false; - for (i = 0; i < NUMBERS.length; ++i) { - if (NUMBERS[_indexnumber][_type]["name"] == _roinew) + for (i = 0; i < NUMBERS[_indexnumber][_type].length; ++i) { + if (NUMBERS[_indexnumber][_type][i]["name"] == _roinew) found = true; } if (found) - return "ROI does already exist, please choose another name!"; + return "ROI name is already existing, please choose another name"; _ret = new Object(); _ret["name"] = _roinew; diff --git a/sd-card/html/reboot_page.html b/sd-card/html/reboot_page.html index 3795296f..f58440b7 100644 --- a/sd-card/html/reboot_page.html +++ b/sd-card/html/reboot_page.html @@ -1,9 +1,8 @@ - + - Reboot - + @@ -22,13 +21,13 @@ p {font-size: 1em;} - -

Do you really want to reboot your ESP32 now?

+ +

Do you really want to reboot now?

- +
diff --git a/sd-card/html/setup.html b/sd-card/html/setup.html index c3383794..3196908a 100644 --- a/sd-card/html/setup.html +++ b/sd-card/html/setup.html @@ -1,17 +1,25 @@ - + - AI on the edge - + - + - - - -

Digitizer - Initial Setup

-

An ESP32 all inclusive neural network recognition system for meter digitalization

-
+ + + + + +
+

Digitizer - AI on the edge - Initial setup

+

An ESP32 all inclusive neural network recognition system for meter digitalization

+
- - - - -
- - - If you need support, have a look to the documentation or the discussion pages. -
+ + + + + + + + + + + +
+ + + + + Setup Progress:
+ +
-
- -
+
+ +
-
- -
+
+ + +
- - - - diff --git a/sd-card/html/style.css b/sd-card/html/style.css index c2b6c3e8..60da015e 100644 --- a/sd-card/html/style.css +++ b/sd-card/html/style.css @@ -1,26 +1,39 @@ body, html { - width: 100%; - height: 100%; - min-height: 800px; + max-width: 1022px; + min-width: 688px; + height: 100vh; + min-height: 100vh; margin: 0px 0px 0px 2px; padding: 0; font-family: arial; - width: fit-content; +} + +@media screen and (max-width:687px) { + body, html { + max-width: 687px; + height: 150vh; + min-height: 100vh; + margin: 0px 0px 0px 2px; + padding: 0; + font-family: arial; + } } .main { display: flex; width: 100%; height: 100%; - flex-direction: column; + flex-direction: column; overflow: hidden; + font-family: arial; } .iframe { - flex-grow: 1; - margin: 5px 0px 4px 0px; + flex: 1 1 auto; + margin: 5px 0px 8px 0px; padding: 0; - border: 2px solid #333; /* black */ + border: 0px solid #333; /* black */ + font-family: arial; } h1 { @@ -41,20 +54,13 @@ p { font-size: 1em; } - - - - - - - .menu { margin: 0px; padding: 0px; font-family: "Arial"; font-size: 18px; font-weight: bold; - width: 1008px; + width: 100%; background: #333; /* black */ } @@ -171,13 +177,6 @@ p { color: white; } - - - - - - - .arrow { border: solid #333; /* black */ border-width: 0 3px 3px 0; @@ -187,9 +186,8 @@ p { } .right { - transform: rotate(-45deg); -webkit-transform: rotate(-45deg); - + transform: rotate(-45deg); position: absolute; right: 10px; top: 20px; @@ -198,8 +196,8 @@ p { } .down { - transform: rotate(45deg); -webkit-transform: rotate(45deg); + transform: rotate(45deg); border-bottom: solid white; border-right: solid white; margin: 0px 0px 2px 5px; diff --git a/sd-card/html/test.html b/sd-card/html/test.html deleted file mode 100644 index 8657156d..00000000 --- a/sd-card/html/test.html +++ /dev/null @@ -1,33 +0,0 @@ - - - - - - - - - testschrift -
- - - - - - diff --git a/sd-card/html/testcnn.html b/sd-card/html/testcnn.html deleted file mode 100644 index ab2b44c2..00000000 --- a/sd-card/html/testcnn.html +++ /dev/null @@ -1 +0,0 @@ -

Not-a-Number

\ No newline at end of file diff --git a/sd-card/html/timezones.html b/sd-card/html/timezones.html index 90cd5249..410c84ce 100644 --- a/sd-card/html/timezones.html +++ b/sd-card/html/timezones.html @@ -1,5 +1,5 @@ - +