Compare commits

...

4 Commits

Author SHA1 Message Date
CaCO3
cf96d49bd0 Release preparations (#3598)
* 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
2025-03-01 00:34:39 +01:00
CaCO3
94a53b38b8 Update Homeassistant discovery (#3580)
* use "total" for Homeassistant discovery topic "raw" if AllowNegativeRates is activ (same as for "value")

* update webUI

* .

* .

* .

* formating

* use state class "measurement" in case of a thermometer

* Update edit_config_template.html

---------

Co-authored-by: CaCO3 <caco@ruinelli.ch>
2025-03-01 00:28:38 +01:00
CaCO3
00ac2130c2 Calculate and validate MD5 on upload (#3590)
* added md5 library

* added MD5 calculation of uploaded file. And return JSON string instead of fileserver

* .

* .

* .

* .

* .

* .

* .

* .

* .

* Add fallback for older firmware

---------

Co-authored-by: CaCO3 <caco@ruinelli.ch>
2025-03-01 00:25:48 +01:00
SybexX
cd1165e547 IgnoreLeadingNaN fix (#3547)
* test1

* test2

* Update edit_config_template.html

* fix

* Update NUMBER.CheckDigitIncreaseConsistency.md

---------

Co-authored-by: CaCO3 <caco3@ruinelli.ch>
2025-03-01 00:09:11 +01:00
17 changed files with 2306 additions and 1944 deletions

View File

@@ -1,37 +1,52 @@
## [16.0.0-RC6] - 2024-02-28
## [16.0.0-RC7] - 2024-03-01
For a full list of changes see [Full list of changes](https://github.com/jomjol/AI-on-the-edge-device/compare/v15.7.0...v16.0.0)
For a full list of changes see [Full list of changes](https://github.com/jomjol/AI-on-the-edge-device/compare/v15.7.0...v16.0.0-RC7)
#### Known issues
Please check the [issues](https://github.com/jomjol/AI-on-the-edge-device/issues) and
[discussions](https://github.com/jomjol/AI-on-the-edge-device/discussions) before reporting a new issue.
#### Homeassistant compatibility
:warning: Please check your Homeassistant instance to make sure it is handled correctly!
#### Core Changes
Only changes since RC5 are listed:
Only changes since RC5 are listed (Skipping RC6):
- [#3436](https://github.com/jomjol/AI-on-the-edge-device/pull/3436) Added basic authentification of the Web Interface and the REST API, see https://jomjol.github.io/AI-on-the-edge-device-docs/Password-Protection
- [#3520](https://github.com/jomjol/AI-on-the-edge-device/pull/3520) Rewrite InfluxDB Interfache
- [#3500](https://github.com/jomjol/AI-on-the-edge-device/pull/3500) Update smartled driver
- [#3454](https://github.com/jomjol/AI-on-the-edge-device/pull/3454) Add thermometer
- [#3538](https://github.com/jomjol/AI-on-the-edge-device/pull/3538) add .gz
- [#3521](https://github.com/jomjol/AI-on-the-edge-device/pull/3521) openmetrics endpoint extension
- [#3537](https://github.com/jomjol/AI-on-the-edge-device/pull/3537) Added data export (CSV files) functionality
- [#3499](https://github.com/jomjol/AI-on-the-edge-device/pull/3499) Added and activated espressif mDNS service
- [#3547](https://github.com/jomjol/AI-on-the-edge-device/pull/3547) Enhanced `IgnoreLeadingNaN`. Marked it in the UI as a per-sequence parameter
- [#3580](https://github.com/jomjol/AI-on-the-edge-device/pull/3580) Update Homeassistant discovery to comply with Homeassistant `2025.2.4`
- [#3565](https://github.com/jomjol/AI-on-the-edge-device/pull/3565) corrected enable/disable texts to make it more intuitive
- [#3538](https://github.com/jomjol/AI-on-the-edge-device/pull/3538), [#3568](https://github.com/jomjol/AI-on-the-edge-device/pull/3568) Compress all Web UI files with gzip
- [#3568](https://github.com/jomjol/AI-on-the-edge-device/pull/3568) compress all HTML files
- [#3590](https://github.com/jomjol/AI-on-the-edge-device/pull/3590) Validate uploaded OTA update file before installing it
- [#3583](https://github.com/jomjol/AI-on-the-edge-device/pull/3583) Remove HTML directory on update
- [#3590](https://github.com/jomjol/AI-on-the-edge-device/pull/3590) Consolidated the `reboot` and `save` buttons -> The `reboot` button ois not part of the notification
corrected enable/disable texts on the configuration page to make it more intuitive
**:warning: Please check your Homeassistant instance to make sure it is handled correctly!**
- [#3520](https://github.com/jomjol/AI-on-the-edge-device/pull/3520) Rewrite InfluxDB Interface
- [#3500](https://github.com/jomjol/AI-on-the-edge-device/pull/3500) Update smart-LED driver
#### Bug Fixes
Only changes since RC5 are listed:
- [#3450](https://github.com/jomjol/AI-on-the-edge-device/pull/3450) fix crash fue to empty CAM parameters in migration
- [#3446](https://github.com/jomjol/AI-on-the-edge-device/pull/3446) fix for incorrect decimal shift
- [#3450](https://github.com/jomjol/AI-on-the-edge-device/pull/3450) fix crash due to empty CAM parameters in migration
- [#3446](https://github.com/jomjol/AI-on-the-edge-device/pull/3446) fix for incorrect decimal shift
## [16.0.0-RC5] - 2024-12-05
For a full list of changes see [Full list of changes](https://github.com/jomjol/AI-on-the-edge-device/compare/v15.7.0...v16.0.0)
For a full list of changes see [Full list of changes](https://github.com/jomjol/AI-on-the-edge-device/compare/v15.7.0...v16.0.0-RC5)
#### Known issues
Please check the [issues](https://github.com/jomjol/AI-on-the-edge-device/issues) and
@@ -62,7 +77,7 @@ Only changes since RC4 are listed:
## [16.0.0-RC4] - 2024-10-06
For a full list of changes see [Full list of changes](https://github.com/jomjol/AI-on-the-edge-device/compare/v15.7.0...v16.0.0-RC1)
For a full list of changes see [Full list of changes](https://github.com/jomjol/AI-on-the-edge-device/compare/v15.7.0...v16.0.0-RC4)
#### Known issues
Please check the [issues](https://github.com/jomjol/AI-on-the-edge-device/issues) and
@@ -81,7 +96,7 @@ Only changes since RC2 are listed:
## [16.0.0-RC3] - 2024-10-05
For a full list of changes see [Full list of changes](https://github.com/jomjol/AI-on-the-edge-device/compare/v15.7.0...v16.0.0-RC1)
For a full list of changes see [Full list of changes](https://github.com/jomjol/AI-on-the-edge-device/compare/v15.7.0...v16.0.0-RC3)
#### Known issues
Please check the [issues](https://github.com/jomjol/AI-on-the-edge-device/issues) and
@@ -98,7 +113,7 @@ Only changes since RC2 are listed:
## [16.0.0-RC2] - 2024-10-04
For a full list of changes see [Full list of changes](https://github.com/jomjol/AI-on-the-edge-device/compare/v15.7.0...v16.0.0-RC1)
For a full list of changes see [Full list of changes](https://github.com/jomjol/AI-on-the-edge-device/compare/v15.7.0...v16.0.0-RC2)
#### Known issues
Please check the [issues](https://github.com/jomjol/AI-on-the-edge-device/issues) and

View File

@@ -0,0 +1,226 @@
/* Src: https://github.com/Zunawe/md5-c, commit: f3529b6
* License: Unlicense */
/*
* Derived from the RSA Data Security, Inc. MD5 Message-Digest Algorithm
* and modified slightly to be functionally identical but condensed into control structures.
*/
#include "md5.h"
/*
* Constants defined by the MD5 algorithm
*/
#define A 0x67452301
#define B 0xefcdab89
#define C 0x98badcfe
#define D 0x10325476
static uint32_t S[] = {7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22,
5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20,
4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23,
6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21};
static uint32_t K[] = {0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee,
0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501,
0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be,
0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821,
0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa,
0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8,
0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed,
0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a,
0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c,
0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70,
0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05,
0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665,
0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039,
0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1,
0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1,
0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391};
/*
* Padding used to make the size (in bits) of the input congruent to 448 mod 512
*/
static uint8_t PADDING[] = {0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
/*
* Bit-manipulation functions defined by the MD5 algorithm
*/
#define F(X, Y, Z) ((X & Y) | (~X & Z))
#define G(X, Y, Z) ((X & Z) | (Y & ~Z))
#define H(X, Y, Z) (X ^ Y ^ Z)
#define I(X, Y, Z) (Y ^ (X | ~Z))
/*
* Rotates a 32-bit word left by n bits
*/
uint32_t rotateLeft(uint32_t x, uint32_t n){
return (x << n) | (x >> (32 - n));
}
/*
* Initialize a context
*/
void md5Init(MD5Context *ctx){
ctx->size = (uint64_t)0;
ctx->buffer[0] = (uint32_t)A;
ctx->buffer[1] = (uint32_t)B;
ctx->buffer[2] = (uint32_t)C;
ctx->buffer[3] = (uint32_t)D;
}
/*
* Add some amount of input to the context
*
* If the input fills out a block of 512 bits, apply the algorithm (md5Step)
* and save the result in the buffer. Also updates the overall size.
*/
void md5Update(MD5Context *ctx, uint8_t *input_buffer, size_t input_len){
uint32_t input[16];
unsigned int offset = ctx->size % 64;
ctx->size += (uint64_t)input_len;
// Copy each byte in input_buffer into the next space in our context input
for(unsigned int i = 0; i < input_len; ++i){
ctx->input[offset++] = (uint8_t)*(input_buffer + i);
// If we've filled our context input, copy it into our local array input
// then reset the offset to 0 and fill in a new buffer.
// Every time we fill out a chunk, we run it through the algorithm
// to enable some back and forth between cpu and i/o
if(offset % 64 == 0){
for(unsigned int j = 0; j < 16; ++j){
// Convert to little-endian
// The local variable `input` our 512-bit chunk separated into 32-bit words
// we can use in calculations
input[j] = (uint32_t)(ctx->input[(j * 4) + 3]) << 24 |
(uint32_t)(ctx->input[(j * 4) + 2]) << 16 |
(uint32_t)(ctx->input[(j * 4) + 1]) << 8 |
(uint32_t)(ctx->input[(j * 4)]);
}
md5Step(ctx->buffer, input);
offset = 0;
}
}
}
/*
* Pad the current input to get to 448 bytes, append the size in bits to the very end,
* and save the result of the final iteration into digest.
*/
void md5Finalize(MD5Context *ctx){
uint32_t input[16];
unsigned int offset = ctx->size % 64;
unsigned int padding_length = offset < 56 ? 56 - offset : (56 + 64) - offset;
// Fill in the padding and undo the changes to size that resulted from the update
md5Update(ctx, PADDING, padding_length);
ctx->size -= (uint64_t)padding_length;
// Do a final update (internal to this function)
// Last two 32-bit words are the two halves of the size (converted from bytes to bits)
for(unsigned int j = 0; j < 14; ++j){
input[j] = (uint32_t)(ctx->input[(j * 4) + 3]) << 24 |
(uint32_t)(ctx->input[(j * 4) + 2]) << 16 |
(uint32_t)(ctx->input[(j * 4) + 1]) << 8 |
(uint32_t)(ctx->input[(j * 4)]);
}
input[14] = (uint32_t)(ctx->size * 8);
input[15] = (uint32_t)((ctx->size * 8) >> 32);
md5Step(ctx->buffer, input);
// Move the result into digest (convert from little-endian)
for(unsigned int i = 0; i < 4; ++i){
ctx->digest[(i * 4) + 0] = (uint8_t)((ctx->buffer[i] & 0x000000FF));
ctx->digest[(i * 4) + 1] = (uint8_t)((ctx->buffer[i] & 0x0000FF00) >> 8);
ctx->digest[(i * 4) + 2] = (uint8_t)((ctx->buffer[i] & 0x00FF0000) >> 16);
ctx->digest[(i * 4) + 3] = (uint8_t)((ctx->buffer[i] & 0xFF000000) >> 24);
}
}
/*
* Step on 512 bits of input with the main MD5 algorithm.
*/
void md5Step(uint32_t *buffer, uint32_t *input){
uint32_t AA = buffer[0];
uint32_t BB = buffer[1];
uint32_t CC = buffer[2];
uint32_t DD = buffer[3];
uint32_t E;
unsigned int j;
for(unsigned int i = 0; i < 64; ++i){
switch(i / 16){
case 0:
E = F(BB, CC, DD);
j = i;
break;
case 1:
E = G(BB, CC, DD);
j = ((i * 5) + 1) % 16;
break;
case 2:
E = H(BB, CC, DD);
j = ((i * 3) + 5) % 16;
break;
default:
E = I(BB, CC, DD);
j = (i * 7) % 16;
break;
}
uint32_t temp = DD;
DD = CC;
CC = BB;
BB = BB + rotateLeft(AA + E + K[i] + input[j], S[i]);
AA = temp;
}
buffer[0] += AA;
buffer[1] += BB;
buffer[2] += CC;
buffer[3] += DD;
}
/*
* Functions that run the algorithm on the provided input and put the digest into result.
* result should be able to store 16 bytes.
*/
void md5String(char *input, uint8_t *result){
MD5Context ctx;
md5Init(&ctx);
md5Update(&ctx, (uint8_t *)input, strlen(input));
md5Finalize(&ctx);
memcpy(result, ctx.digest, 16);
}
void md5File(FILE *file, uint8_t *result){
void *input_buffer = malloc(1024);
size_t input_size = 0;
MD5Context ctx;
md5Init(&ctx);
while((input_size = fread(input_buffer, 1, 1024, file)) > 0){
md5Update(&ctx, (uint8_t *)input_buffer, input_size);
}
md5Finalize(&ctx);
free(input_buffer);
memcpy(result, ctx.digest, 16);
}

View File

@@ -0,0 +1,28 @@
/* Src: https://github.com/Zunawe/md5-c, commit: f3529b6
* License: Unlicense */
#pragma once
#ifndef MD5_H
#define MD5_H
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
typedef struct{
uint64_t size; // Size of input in bytes
uint32_t buffer[4]; // Current accumulation of hash
uint8_t input[64]; // Input to be used in the next step
uint8_t digest[16]; // Result of algorithm
}MD5Context;
void md5Init(MD5Context *ctx);
void md5Update(MD5Context *ctx, uint8_t *input, size_t input_len);
void md5Finalize(MD5Context *ctx);
void md5Step(uint32_t *buffer, uint32_t *input);
void md5String(char *input, uint8_t *result);
void md5File(FILE *file, uint8_t *result);
#endif // MD5_H

View File

@@ -36,6 +36,7 @@ extern "C" {
#include "MainFlowControl.h"
#include "server_help.h"
#include "md5.h"
#ifdef ENABLE_MQTT
#include "interface_mqtt.h"
#endif //ENABLE_MQTT
@@ -610,6 +611,8 @@ static esp_err_t upload_post_handler(httpd_req_t *req)
FILE *fd = NULL;
struct stat file_stat;
ESP_LOGI(TAG, "uri: %s", req->uri);
httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*");
/* Skip leading "/upload" from URI to get filename */
@@ -711,43 +714,76 @@ static esp_err_t upload_post_handler(httpd_req_t *req)
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "File saved: " + string(filename));
ESP_LOGI(TAG, "File reception completed");
std::string directory = std::string(filepath);
size_t zw = directory.find("/");
size_t found = zw;
while (zw != std::string::npos)
{
zw = directory.find("/", found+1);
if (zw != std::string::npos)
found = zw;
}
string s = req->uri;
if (isInString(s, "?md5")) {
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Calculate and return MD5 sum...");
fd = fopen(filepath, "r");
if (!fd) {
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Failed to open file for reading: " + string(filepath));
/* Respond with 500 Internal Server Error */
httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Failed to open file for reading");
return ESP_FAIL;
}
int start_fn = strlen(((struct file_server_data *)req->user_ctx)->base_path);
ESP_LOGD(TAG, "Directory: %s, start_fn: %d, found: %d", directory.c_str(), start_fn, found);
directory = directory.substr(start_fn, found - start_fn + 1);
directory = "/fileserver" + directory;
// ESP_LOGD(TAG, "Directory danach 2: %s", directory.c_str());
uint8_t result[16];
string md5hex = "";
string response = "{\"md5\":";
char hex[3];
/* Redirect onto root to see the updated file list */
if (strcmp(filename, "/config/config.ini") == 0 ||
strcmp(filename, "/config/ref0.jpg") == 0 ||
strcmp(filename, "/config/ref0_org.jpg") == 0 ||
strcmp(filename, "/config/ref1.jpg") == 0 ||
strcmp(filename, "/config/ref1_org.jpg") == 0 ||
strcmp(filename, "/config/reference.jpg") == 0 ||
strcmp(filename, "/img_tmp/ref0.jpg") == 0 ||
strcmp(filename, "/img_tmp/ref0_org.jpg") == 0 ||
strcmp(filename, "/img_tmp/ref1.jpg") == 0 ||
strcmp(filename, "/img_tmp/ref1_org.jpg") == 0 ||
strcmp(filename, "/img_tmp/reference.jpg") == 0 )
{
httpd_resp_set_status(req, HTTPD_200); // Avoid reloading of folder content
}
else {
httpd_resp_set_status(req, "303 See Other"); // Reload folder content after upload
md5File(fd, result);
fclose(fd);
for (int i = 0; i < sizeof(result); i++) {
snprintf(hex, sizeof(hex), "%02x", result[i]);
md5hex.append(hex);
}
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "MD5 of " + string(filepath) + ": " + md5hex);
response.append("\"" + md5hex + "\"");
response.append("}");
httpd_resp_sendstr(req, response.c_str());
}
else { // Return file server page
std::string directory = std::string(filepath);
size_t zw = directory.find("/");
size_t found = zw;
while (zw != std::string::npos)
{
zw = directory.find("/", found+1);
if (zw != std::string::npos)
found = zw;
}
httpd_resp_set_hdr(req, "Location", directory.c_str());
httpd_resp_sendstr(req, "File uploaded successfully");
int start_fn = strlen(((struct file_server_data *)req->user_ctx)->base_path);
ESP_LOGD(TAG, "Directory: %s, start_fn: %d, found: %d", directory.c_str(), start_fn, found);
directory = directory.substr(start_fn, found - start_fn + 1);
directory = "/fileserver" + directory;
// ESP_LOGD(TAG, "Directory danach 2: %s", directory.c_str());
/* Redirect onto root to see the updated file list */
if (strcmp(filename, "/config/config.ini") == 0 ||
strcmp(filename, "/config/ref0.jpg") == 0 ||
strcmp(filename, "/config/ref0_org.jpg") == 0 ||
strcmp(filename, "/config/ref1.jpg") == 0 ||
strcmp(filename, "/config/ref1_org.jpg") == 0 ||
strcmp(filename, "/config/reference.jpg") == 0 ||
strcmp(filename, "/img_tmp/ref0.jpg") == 0 ||
strcmp(filename, "/img_tmp/ref0_org.jpg") == 0 ||
strcmp(filename, "/img_tmp/ref1.jpg") == 0 ||
strcmp(filename, "/img_tmp/ref1_org.jpg") == 0 ||
strcmp(filename, "/img_tmp/reference.jpg") == 0 )
{
httpd_resp_set_status(req, HTTPD_200); // Avoid reloading of folder content
}
else {
httpd_resp_set_status(req, "303 See Other"); // Reload folder content after upload
}
httpd_resp_set_hdr(req, "Location", directory.c_str());
httpd_resp_sendstr(req, "File uploaded successfully");
}
return ESP_OK;
}

View File

@@ -31,7 +31,6 @@ enum t_RateType {
RateChange // time difference is considered and a normalized rate is used for comparison with NumberPost.maxRate
};
/**
* Holds all properties and settings of a sequence. A sequence is a set of digit and/or analog ROIs that are combined to
* provide one meter reading (value).
@@ -45,6 +44,7 @@ struct NumberPost {
int ChangeRateThreshold; // threshold parameter for negative rate detection
bool PreValueOkay; // previousValueValid; indicates that the reading of the previous round has no errors
bool AllowNegativeRates; // allowNegativeRate; defines if the consistency checks allow negative rates between consecutive meter readings.
bool IgnoreLeadingNaN;
bool checkDigitIncreaseConsistency; // extendedConsistencyCheck; performs an additional consistency check to avoid wrong readings
time_t timeStampLastValue; // Timestamp for the last read value; is used for the log
time_t timeStampLastPreValue; // Timestamp for the last PreValue set; is used for useMaxRateValue
@@ -66,7 +66,7 @@ struct NumberPost {
float AnalogToDigitTransitionStart; // AnalogToDigitTransitionStartValue; FIXME: need a better description; When is the digit > x.1, i.e. when does it start to tilt?
int Nachkomma; // decimalPlaces; usually defined by the number of analog ROIs; affected by DecimalShift
string DomoticzIdx; // Domoticz counter Idx
string DomoticzIdx; // Domoticz counter Idx
string FieldV1; // influxdbFieldName_v1; Name of the Field in InfluxDBv1
string MeasurementV1; // influxdbMeasurementName_v1; Name of the Measurement in InfluxDBv1
@@ -83,4 +83,3 @@ struct NumberPost {
};
#endif

View File

@@ -156,7 +156,7 @@ bool ClassFlowMQTT::ReadParameter(FILE* pfile, string& aktparamgraph)
mqttServer_setMeterType("water", "L", "h", "L/h");
}
else if (toUpper(splitted[1]) == "WATER_FT3") {
mqttServer_setMeterType("water", "ft³", "m", "ft³/m"); // m = Minutes
mqttServer_setMeterType("water", "ft³", "min", "ft³/min"); // min = Minutes
}
else if (toUpper(splitted[1]) == "WATER_GAL") {
mqttServer_setMeterType("water", "gal", "h", "gal/h");
@@ -165,7 +165,7 @@ bool ClassFlowMQTT::ReadParameter(FILE* pfile, string& aktparamgraph)
mqttServer_setMeterType("gas", "", "h", "m³/h");
}
else if (toUpper(splitted[1]) == "GAS_FT3") {
mqttServer_setMeterType("gas", "ft³", "m", "ft³/m"); // m = Minutes
mqttServer_setMeterType("gas", "ft³", "min", "ft³/min"); // min = Minutes
}
else if (toUpper(splitted[1]) == "ENERGY_WH") {
mqttServer_setMeterType("energy", "Wh", "h", "W");
@@ -180,13 +180,13 @@ bool ClassFlowMQTT::ReadParameter(FILE* pfile, string& aktparamgraph)
mqttServer_setMeterType("energy", "GJ", "h", "GJ/h");
}
else if (toUpper(splitted[1]) == "TEMPERATURE_C") {
mqttServer_setMeterType("temperature", "°C", "m", "°C/m"); // m = Minutes
mqttServer_setMeterType("temperature", "°C", "min", "°C/min"); // min = Minutes
}
else if (toUpper(splitted[1]) == "TEMPERATURE_F") {
mqttServer_setMeterType("temperature", "°F", "m", "°F/m"); // m = Minutes
mqttServer_setMeterType("temperature", "°F", "min", "°F/min"); // min = Minutes
}
else if (toUpper(splitted[1]) == "TEMPERATURE_K") {
mqttServer_setMeterType("temperature", "K", "m", "K/m"); // m = Minutes
mqttServer_setMeterType("temperature", "K", "min", "K/m"); // min = Minutes
}
}

View File

@@ -320,7 +320,6 @@ ClassFlowPostProcessing::ClassFlowPostProcessing(std::vector<ClassFlow*>* lfc, C
ListFlowControll = lfc;
flowTakeImage = NULL;
UpdatePreValueINI = false;
IgnoreLeadingNaN = false;
flowAnalog = _analog;
flowDigit = _digit;
@@ -431,6 +430,27 @@ void ClassFlowPostProcessing::handleAllowNegativeRate(string _decsep, string _va
}
}
void ClassFlowPostProcessing::handleIgnoreLeadingNaN(string _decsep, string _value) {
string _digit, _decpos;
int _pospunkt = _decsep.find_first_of(".");
if (_pospunkt > -1) {
_digit = _decsep.substr(0, _pospunkt);
}
else {
_digit = "default";
}
for (int j = 0; j < NUMBERS.size(); ++j) {
bool _zwdc = alphanumericToBoolean(_value);
// Set to default first (if nothing else is set)
if ((_digit == "default") || (NUMBERS[j]->name == _digit)) {
NUMBERS[j]->IgnoreLeadingNaN = _zwdc;
}
}
}
void ClassFlowPostProcessing::handleMaxRateType(string _decsep, string _value) {
string _digit, _decpos;
int _pospunkt = _decsep.find_first_of(".");
@@ -509,7 +529,7 @@ void ClassFlowPostProcessing::handleChangeRateThreshold(string _decsep, string _
}
}
}
/*
void ClassFlowPostProcessing::handlecheckDigitIncreaseConsistency(std::string _decsep, std::string _value)
{
std::string _digit;
@@ -532,7 +552,7 @@ void ClassFlowPostProcessing::handlecheckDigitIncreaseConsistency(std::string _d
}
}
}
*/
bool ClassFlowPostProcessing::ReadParameter(FILE* pfile, string& aktparamgraph) {
std::vector<string> splitted;
int _n;
@@ -585,12 +605,7 @@ bool ClassFlowPostProcessing::ReadParameter(FILE* pfile, string& aktparamgraph)
}
if ((toUpper(_param) == "CHECKDIGITINCREASECONSISTENCY") && (splitted.size() > 1)) {
// handlecheckDigitIncreaseConsistency(splitted[0], splitted[1]);
if (alphanumericToBoolean(splitted[1])) {
for (_n = 0; _n < NUMBERS.size(); ++_n) {
NUMBERS[_n]->checkDigitIncreaseConsistency = true;
}
}
handlecheckDigitIncreaseConsistency(splitted[0], splitted[1]);
}
if ((toUpper(_param) == "ALLOWNEGATIVERATES") && (splitted.size() > 1)) {
@@ -602,7 +617,7 @@ bool ClassFlowPostProcessing::ReadParameter(FILE* pfile, string& aktparamgraph)
}
if ((toUpper(_param) == "IGNORELEADINGNAN") && (splitted.size() > 1)) {
IgnoreLeadingNaN = alphanumericToBoolean(splitted[1]);
handleIgnoreLeadingNaN(splitted[0], splitted[1]);
}
if ((toUpper(_param) == "PREVALUEAGESTARTUP") && (splitted.size() > 1)) {
@@ -670,6 +685,7 @@ void ClassFlowPostProcessing::InitNUMBERS() {
_number->FlowRateAct = 0; // m3 / min
_number->PreValueOkay = false;
_number->AllowNegativeRates = false;
_number->IgnoreLeadingNaN = false;
_number->MaxRateValue = 0.1;
_number->MaxRateType = AbsoluteChange;
_number->useMaxRateValue = false;
@@ -821,7 +837,7 @@ bool ClassFlowPostProcessing::doFlow(string zwtime) {
ESP_LOGD(TAG, "After ShiftDecimal: ReturnRaw %s", NUMBERS[j]->ReturnRawValue.c_str());
#endif
if (IgnoreLeadingNaN) {
if (NUMBERS[j]->IgnoreLeadingNaN) {
while ((NUMBERS[j]->ReturnRawValue.length() > 1) && (NUMBERS[j]->ReturnRawValue[0] == 'N')) {
NUMBERS[j]->ReturnRawValue.erase(0, 1);
}
@@ -868,12 +884,7 @@ bool ClassFlowPostProcessing::doFlow(string zwtime) {
if (NUMBERS[j]->checkDigitIncreaseConsistency) {
if (flowDigit) {
if (flowDigit->getCNNType() != Digit) {
ESP_LOGD(TAG, "checkDigitIncreaseConsistency = true - ignored due to wrong CNN-Type (not Digit Classification)");
}
else {
NUMBERS[j]->Value = checkDigitConsistency(NUMBERS[j]->Value, NUMBERS[j]->DecimalShift, NUMBERS[j]->analog_roi != NULL, NUMBERS[j]->PreValue);
}
NUMBERS[j]->Value = checkDigitConsistency(NUMBERS[j]->Value, NUMBERS[j]->DecimalShift, NUMBERS[j]->analog_roi != NULL, NUMBERS[j]->PreValue);
}
else {
#ifdef SERIAL_DEBUG

View File

@@ -10,7 +10,6 @@
#include <string>
class ClassFlowPostProcessing :
public ClassFlow
{
@@ -19,8 +18,7 @@ protected:
int PreValueAgeStartup;
bool ErrorMessage;
bool IgnoreLeadingNaN; // SPECIAL CASE for User Gustl ???
ClassFlowCNNGeneral* flowAnalog;
ClassFlowCNNGeneral* flowDigit;
@@ -35,15 +33,16 @@ protected:
float checkDigitConsistency(double input, int _decilamshift, bool _isanalog, double _preValue);
void InitNUMBERS();
void handleDecimalSeparator(string _decsep, string _value);
void handleMaxRateValue(string _decsep, string _value);
void handleDecimalExtendedResolution(string _decsep, string _value);
void handleMaxRateType(string _decsep, string _value);
void handleAnalogToDigitTransitionStart(string _decsep, string _value);
void handleAllowNegativeRate(string _decsep, string _value);
void handleIgnoreLeadingNaN(string _decsep, string _value);
void handleChangeRateThreshold(string _decsep, string _value);
std::string GetStringReadouts(general);
void handlecheckDigitIncreaseConsistency(std::string _decsep, std::string _value);
void WriteDataLog(int _index);
@@ -75,5 +74,4 @@ public:
string name(){return "ClassFlowPostProcessing";};
};
#endif //CLASSFFLOWPOSTPROCESSING_H

View File

@@ -195,9 +195,12 @@ bool MQTThomeassistantDiscovery(int qos) {
/* If "Allow neg. rate" is true, use "measurement" instead of "total_increasing" for the State Class, see https://github.com/jomjol/AI-on-the-edge-device/issues/3331 */
std::string value_state_class = "total_increasing";
if ((*NUMBERS)[i]->AllowNegativeRates) {
if (meterType == "temperature") {
value_state_class = "measurement";
}
else if ((*NUMBERS)[i]->AllowNegativeRates) {
value_state_class = "total";
}
/* Energy meters need a different Device Class, see https://github.com/jomjol/AI-on-the-edge-device/issues/3333 */
std::string rate_device_class = "volume_flow_rate";
@@ -205,17 +208,17 @@ bool MQTThomeassistantDiscovery(int qos) {
rate_device_class = "power";
}
// Group | Field | User Friendly Name | Icon | Unit | Device Class | State Class | Entity Category
allSendsSuccessed |= sendHomeAssistantDiscoveryTopic(group, "value", "Value", "gauge", valueUnit, meterType, value_state_class, "", qos); // State Class = "total_increasing" if <NUMBERS>.AllowNegativeRates = false, else use "measurement"
allSendsSuccessed |= sendHomeAssistantDiscoveryTopic(group, "raw", "Raw Value", "raw", valueUnit, meterType, "measurement", "diagnostic", qos);
allSendsSuccessed |= sendHomeAssistantDiscoveryTopic(group, "error", "Error", "alert-circle-outline", "", "", "", "diagnostic", qos);
// Group | Field | User Friendly Name | Icon | Unit | Device Class | State Class | Entity Category | QoS
allSendsSuccessed |= sendHomeAssistantDiscoveryTopic(group, "value", "Value", "gauge", valueUnit, meterType, value_state_class, "", qos); // State Class = "total_increasing" if <NUMBERS>.AllowNegativeRates = false, "measurement" in case of a thermometer, else use "total".
allSendsSuccessed |= sendHomeAssistantDiscoveryTopic(group, "raw", "Raw Value", "raw", valueUnit, meterType, value_state_class, "diagnostic", qos);
allSendsSuccessed |= sendHomeAssistantDiscoveryTopic(group, "error", "Error", "alert-circle-outline", "", "", "", "diagnostic", qos);
/* Not announcing "rate" as it is better to use rate_per_time_unit resp. rate_per_digitization_round */
// allSendsSuccessed |= sendHomeAssistantDiscoveryTopic(group, "rate", "Rate (Unit/Minute)", "swap-vertical", "", "", "", ""); // Legacy, always Unit per Minute
allSendsSuccessed |= sendHomeAssistantDiscoveryTopic(group, "rate_per_time_unit", "Rate (" + rateUnit + ")", "swap-vertical", rateUnit, rate_device_class, "measurement", "", qos);
allSendsSuccessed |= sendHomeAssistantDiscoveryTopic(group, "rate_per_digitization_round", "Change since last Digitization round", "arrow-expand-vertical", valueUnit, "", "measurement", "", qos); // correctly the Unit is Unit/Interval!
allSendsSuccessed |= sendHomeAssistantDiscoveryTopic(group, "timestamp", "Timestamp", "clock-time-eight-outline", "", "timestamp", "", "diagnostic", qos);
allSendsSuccessed |= sendHomeAssistantDiscoveryTopic(group, "json", "JSON", "code-json", "", "", "", "diagnostic", qos);
allSendsSuccessed |= sendHomeAssistantDiscoveryTopic(group, "problem", "Problem", "alert-outline", "", "problem", "", "", qos); // Special binary sensor which is based on error topic
// allSendsSuccessed |= sendHomeAssistantDiscoveryTopic(group, "rate", "Rate (Unit/Minute)", "swap-vertical", "", "", "", "", qos); // Legacy, always Unit per Minute
allSendsSuccessed |= sendHomeAssistantDiscoveryTopic(group, "rate_per_time_unit", "Rate (" + rateUnit + ")", "swap-vertical", rateUnit, rate_device_class, "measurement", "", qos);
allSendsSuccessed |= sendHomeAssistantDiscoveryTopic(group, "rate_per_digitization_round","Change since last Digitization round", "arrow-expand-vertical", valueUnit, "", "measurement", "", qos); // correctly the Unit is Unit/Interval!
allSendsSuccessed |= sendHomeAssistantDiscoveryTopic(group, "timestamp", "Timestamp", "clock-time-eight-outline", "", "timestamp", "", "diagnostic", qos);
allSendsSuccessed |= sendHomeAssistantDiscoveryTopic(group, "json", "JSON", "code-json", "", "", "", "diagnostic", qos);
allSendsSuccessed |= sendHomeAssistantDiscoveryTopic(group, "problem", "Problem", "alert-outline", "", "problem", "", "", qos); // Special binary sensor which is based on error topic
}
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Successfully published all Homeassistant Discovery MQTT topics");

View File

@@ -31,7 +31,6 @@ AlignmentAlgo
CNNGoodThreshold
PreValueAgeStartup
ErrorMessage
CheckDigitIncreaseConsistency
IO0
IO1
IO3

View File

@@ -3,9 +3,26 @@ Default Value: `other`
Select the Meter Type so the sensors have the right units in Homeassistant.
!!! Note
For `Watermeter` you need to have Homeassistant 2022.11 or newer!
Please also make sure that the selected Meter Type matches the dimension of the value provided by the meter!
Eg. if your meter provides `m³`, you need to also set it to `m³`.
Alternatively you can set the parameter `DecimalShift` to `3` so the value is converted to `liters`!
List of supported options:
- `other`
- `water_m3` (uses `m^3/min` as rate)
- `water_l` (uses `l/h` as rate, not officially supported by Homeassistant!)
- `water_gal` (uses `gal/h` as rate, not officially supported by Homeassistant!)
- `water_ft3` (uses `ft^3/min` as rate)
- `gas_m3` (uses `m^3/min` as rate)
- `gas_ft3` (uses `ft^3/min` as rate)
- `energy_wh` (uses `W` as rate)
- `energy_kwh` (uses `KW` as rate)
- `energy_mwh` (uses `MW` as rate)
- `energy_gj` (uses `GJ/h` as rate, not officially supported by Homeassistant!)
- `temperature_c` (uses `+C/min` as rate)
- `temperature_f` (uses `°F/min` as rate)
- `temperature_k` (uses `K/min` as rate)
!!! Note
Not all options are supported by Homeassistant, see `SensorDeviceClass.VOLUME_FLOW_RATE` in [https://developers.home-assistant.io/docs/core/entity/sensor/#available-device-classes](https://developers.home-assistant.io/docs/core/entity/sensor/#available-device-classes)!

View File

@@ -6,3 +6,6 @@ Default Value: `false`
An additional consistency check.
It especially improves the zero crossing check between digits.
!!! Note
This parameter must be prefixed with `<NUMBER>` followed by a dot (eg. `main.CheckDigitIncreaseConsistency`). `<NUMBER>` is the name of the number sequence defined in the ROI's.

View File

@@ -1,6 +1,7 @@
/* The UI can also be run locally, but you have to set the IP of your devide accordingly.
/* The UI can also be run locally, but you have to set the IP of your device accordingly.
* And you also might have to disable CORS in your webbrowser!
* Eg using https://chromewebstore.google.com/detail/allow-cors-access-control/lhobafahddgcelffkeicbaginigeejlf?utm_source=ext_app_menu on chrome
* Keep empty to disable using it. Enabling it will break access through a forwared port, see
* https://github.com/jomjol/AI-on-the-edge-device/issues/2681 */
var domainname_for_testing = "";

File diff suppressed because it is too large Load Diff

17
sd-card/html/md5.min.js vendored Normal file

File diff suppressed because one or more lines are too long

View File

@@ -25,6 +25,7 @@
</style>
<link href="firework.css?v=$COMMIT_HASH" rel="stylesheet">
<script src="md5.min.js?v=$COMMIT_HASH"></script>
<script type="text/javascript" src="jquery-3.6.0.min.js?v=$COMMIT_HASH"></script>
<script type="text/javascript" src="common.js?v=$COMMIT_HASH"></script>
<script type="text/javascript" src="firework.js?v=$COMMIT_HASH"></script>
@@ -142,7 +143,7 @@
function doRebootAfterUpdate() {
var xhttp = new XMLHttpRequest();
xhttp.open("GET", "/reboot", true);
xhttp.open("GET", domainname + "/reboot", true);
xhttp.send();
}
@@ -169,15 +170,35 @@
}
};
var _toDo = domainname + "/ota?task=emptyfirmwaredir";
xhttp.open("GET", _toDo, true);
var url = domainname + "/ota?task=emptyfirmwaredir";
xhttp.open("GET", url, true);
xhttp.send();
}
function extract() {
document.getElementById("status").innerText = "Status: Processing on device...";
function validateMd5(md5_on_device, callback) {
const reader = new FileReader();
reader.onload = (event) => {
const fileContent = event.target.result;
md5_on_webbrowser = md5(fileContent);
console.log("MD5 on device: " + md5_on_device + ", MD5 on web browser: " + md5_on_webbrowser);
if (md5_on_device == md5_on_webbrowser) {
console.log("MD5 values are equal");
callback(true);
}
else {
console.log("MD5 values are NOT equal!");
callback(false);
}
}
var fileInput = document.getElementById("file_selector").files;
reader.readAsArrayBuffer(fileInput[0]);
}
function extract() {
var xhttp = new XMLHttpRequest();
/* first delete the old firmware */
xhttp.onreadystatechange = function() {
@@ -186,9 +207,9 @@
document.cookie = "page=overview.html?v=$COMMIT_HASH" + "; path=/"; // Make sure after the reboot we go to the overview page
if (xhttp.responseText.startsWith("reboot")) { // Reboot required
console.log("Upload completed, the device will now restart and install the update!");
console.log("The device will now reboot and install the update!");
document.getElementById("status").innerText = "Status: Installing...";
firework.launch('Upload completed, the device will now restart and install the update', 'success', 5000);
firework.launch('Upload completed and validated. The device will now reboot and install the update', 'success', 5000);
/* Tell it to reboot */
doRebootAfterUpdate();
@@ -228,8 +249,8 @@
var file_name = document.getElementById("file_selector").value;
filePath = file_name.split(/[\\\/]/).pop();
var _toDo = domainname + "/ota?task=update&file=" + filePath;
xhttp.open("GET", _toDo, true);
var url = domainname + "/ota?task=update&file=" + filePath;
xhttp.open("GET", url, true);
xhttp.send();
}
@@ -242,7 +263,7 @@
function upload() {
document.getElementById("status").innerText = "Status: Uploading...";
var upload_path = "/upload/firmware/" + filePath;
var url = domainname + "/upload/firmware/" + filePath + "?md5";
var file = _("file_selector").files[0];
var formdata = new FormData();
@@ -253,7 +274,7 @@
ajax.addEventListener("error", errorHandler, false);
ajax.addEventListener("abort", abortHandler, false);
ajax.open("POST", upload_path);
ajax.open("POST", url);
ajax.send(file);
}
@@ -263,16 +284,43 @@
" 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...";
if (Math.round(percent) == 100) {
_("progressBar").value = 0; //will clear progress bar after successful upload
_("loaded_n_total").innerHTML = "";
_("status").innerHTML = "Status: Upload completed. Validating file...";
}
else {
_("status").innerHTML = "Status: " + Math.round(percent) + "% uploaded...";
}
}
function completeHandler(event) {
_("status").innerHTML = "Status: " + event.target.responseText;
_("progressBar").value = 0; //will clear progress bar after successful upload
_("loaded_n_total").innerHTML = "";
extract();
console.log("Upload completed");
console.log("Response: " + event.target.responseText);
try {
md5_on_device = JSON.parse(event.target.responseText).md5;
validateMd5(md5_on_device, (result) => {
if (result == true) {
_("status").innerHTML = "Status: The uploaded file is valid, installing it...";
extract();
}
else {
_("status").innerHTML = "Status: The file got corrupted! Please upload it again!";
firework.launch('Upload failed, the file got corrupted! Please upload it again!', 'danger', 30000);
document.getElementById("start_OTA_button").disabled = false;
}
});
}
catch (e) {
// If the firmware is to old, it will return the file sever page instead of the JSON object with the MD5 sum.
// In juch case just proceed to keep legacy support.
console.log("It seems to be a legacy firmware, installing the update without validation!");
_("status").innerHTML = "Status: It seems to be a legacy firmware, installing the update without validation...";
extract();
}
}

View File

@@ -6,7 +6,6 @@ var ref = new Array(2);
var NUMBERS = new Array(0);
var REFERENCES = new Array(0);
function getNUMBERSList() {
_domainname = getDomainname();
var namenumberslist = "";
@@ -33,7 +32,6 @@ function getNUMBERSList() {
return namenumberslist;
}
function getDATAList() {
_domainname = getDomainname();
datalist = "";
@@ -62,7 +60,6 @@ function getDATAList() {
return datalist;
}
function getTFLITEList() {
_domainname = getDomainname();
tflitelist = "";
@@ -90,7 +87,6 @@ function getTFLITEList() {
return tflitelist;
}
function ParseConfig() {
config_split = config_gesamt.split("\n");
var aktline = 0;
@@ -172,7 +168,7 @@ function ParseConfig() {
category[catname]["enabled"] = false;
category[catname]["found"] = false;
param[catname] = new Object();
ParamAddValue(param, catname, "DecimalShift", 1, true);
ParamAddValue(param, catname, "DecimalShift", 1, true, "0");
ParamAddValue(param, catname, "AnalogToDigitTransitionStart", 1, true, "9.2");
ParamAddValue(param, catname, "ChangeRateThreshold", 1, true, "2");
// ParamAddValue(param, catname, "PreValueUse", 1, true, "true");
@@ -185,7 +181,7 @@ function ParseConfig() {
ParamAddValue(param, catname, "IgnoreLeadingNaN", 1, true, "false");
// ParamAddValue(param, catname, "IgnoreAllNaN", 1, true, "false");
ParamAddValue(param, catname, "ErrorMessage");
ParamAddValue(param, catname, "CheckDigitIncreaseConsistency");
ParamAddValue(param, catname, "CheckDigitIncreaseConsistency", 1, true, "false");
var catname = "MQTT";
category[catname] = new Object();
@@ -359,7 +355,6 @@ function ParseConfig() {
}
}
function ParamAddValue(param, _cat, _param, _anzParam = 1, _isNUMBER = false, _defaultValue = "", _checkRegExList = null) {
param[_cat][_param] = new Object();
param[_cat][_param]["found"] = false;
@@ -371,7 +366,6 @@ function ParamAddValue(param, _cat, _param, _anzParam = 1, _isNUMBER = false, _d
param[_cat][_param].checkRegExList = _checkRegExList;
};
function ParseConfigParamAll(_aktline, _catname) {
++_aktline;
@@ -403,7 +397,6 @@ function ParseConfigParamAll(_aktline, _catname) {
return _aktline;
}
function ParamExtractValue(_param, _linesplit, _catname, _paramname, _aktline, _iscom, _anzvalue = 1) {
if ((_linesplit[0].toUpperCase() == _paramname.toUpperCase()) && (_linesplit.length > _anzvalue)) {
_param[_catname][_paramname]["found"] = true;
@@ -417,7 +410,6 @@ function ParamExtractValue(_param, _linesplit, _catname, _paramname, _aktline, _
}
}
function ParamExtractValueAll(_param, _linesplit, _catname, _aktline, _iscom) {
for (var paramname in _param[_catname]) {
_AktROI = "default";
@@ -475,7 +467,6 @@ function ParamExtractValueAll(_param, _linesplit, _catname, _aktline, _iscom) {
}
}
function getCamConfig() {
ParseConfig();
@@ -653,12 +644,10 @@ function getCamConfig() {
return param;
}
function getConfigParameters() {
return param;
}
function WriteConfigININew() {
// Cleanup empty NUMBERS
for (var j = 0; j < NUMBERS.length; ++j) {
@@ -760,7 +749,6 @@ function WriteConfigININew() {
}
}
function isCommented(input) {
let isComment = false;
@@ -772,7 +760,6 @@ function isCommented(input) {
return [isComment, input];
}
function SaveConfigToServer(_domainname){
// leere Zeilen am Ende löschen
var zw = config_split.length - 1;
@@ -792,17 +779,14 @@ function SaveConfigToServer(_domainname){
FileSendContent(config_gesamt, "/config/config.ini", _domainname);
}
function getConfig() {
return config_gesamt;
}
function getConfigCategory() {
return category;
}
function ExtractROIs(_aktline, _type){
var linesplit = ZerlegeZeile(_aktline);
abc = getNUMBERS(linesplit[0], _type);
@@ -819,7 +803,6 @@ function ExtractROIs(_aktline, _type){
}
}
function getNUMBERS(_name, _type, _create = true) {
_pospunkt = _name.indexOf (".");
@@ -879,7 +862,6 @@ function getNUMBERS(_name, _type, _create = true) {
return neuroi;
}
function CopyReferenceToImgTmp(_domainname) {
for (index = 0; index < 2; ++index) {
_filenamevon = REFERENCES[index]["name"];
@@ -894,12 +876,10 @@ function CopyReferenceToImgTmp(_domainname) {
}
}
function GetReferencesInfo(){
return REFERENCES;
}
function UpdateConfigReferences(_domainname){
for (var index = 0; index < 2; ++index) {
_filenamenach = REFERENCES[index]["name"];
@@ -914,7 +894,6 @@ function UpdateConfigReferences(_domainname){
}
}
function UpdateConfigReference(_anzneueref, _domainname){
var index = 0;
@@ -939,12 +918,10 @@ function UpdateConfigReference(_anzneueref, _domainname){
FileCopyOnServer(_filenamevon, _filenamenach, _domainname);
}
function getNUMBERInfo(){
return NUMBERS;
}
function RenameNUMBER(_alt, _neu){
if ((_neu.indexOf(".") >= 0) || (_neu.indexOf(",") >= 0) || (_neu.indexOf(" ") >= 0) || (_neu.indexOf("\"") >= 0)) {
return "Number sequence name must not contain , . \" or a space";
@@ -972,7 +949,6 @@ function RenameNUMBER(_alt, _neu){
return "";
}
function DeleteNUMBER(_delete){
if (NUMBERS.length == 1) {
return "One number sequence is mandatory. Therefore this cannot be deleted"
@@ -993,7 +969,6 @@ function DeleteNUMBER(_delete){
return "";
}
function CreateNUMBER(_numbernew){
found = false;
@@ -1041,7 +1016,6 @@ function CreateNUMBER(_numbernew){
return "";
}
function getROIInfo(_typeROI, _number){
index = -1;
@@ -1059,7 +1033,6 @@ function getROIInfo(_typeROI, _number){
}
}
function RenameROI(_number, _type, _alt, _neu){
if ((_neu.includes("=")) || (_neu.includes(".")) || (_neu.includes(":")) || (_neu.includes(",")) || (_neu.includes(";")) || (_neu.includes(" ")) || (_neu.includes("\""))) {
return "ROI name must not contain . : , ; = \" or space";
@@ -1098,7 +1071,6 @@ function RenameROI(_number, _type, _alt, _neu){
return "";
}
function DeleteNUMBER(_delte) {
if (NUMBERS.length == 1) {
return "The last number cannot be deleted"
@@ -1119,7 +1091,6 @@ function DeleteNUMBER(_delte) {
return "";
}
function CreateROI(_number, _type, _pos, _roinew, _x, _y, _dx, _dy, _CCW){
_indexnumber = -1;