htmlinfo;
+
+ result = "Found ROIs:

\n";
+ result = result + "Analog Pointers: ";
+
+ htmlinfo = GetHTMLInfo();
+ for (int i = 0; i < htmlinfo.size(); ++i)
+ {
+ std::stringstream stream;
+ stream << std::fixed << std::setprecision(1) << htmlinfo[i]->val;
+ zw = stream.str();
+
+ result = result + "
filename + "\"> " + zw;
+ delete htmlinfo[i];
+ }
+ htmlinfo.clear();
+
+ return result;
+}
+
+
+
+bool ClassFlowAnalog::doFlow(string time)
+{
+ if (disabled)
+ return true;
+
+ if (!doAlignAndCut(time)){
+ return false;
+ };
+
+ if (debugdetailanalog) LogFile.WriteToFile("ClassFlowAnalog::doFlow nach Alignment");
+
+ doNeuralNetwork(time);
+
+ RemoveOldLogs();
+
+ return true;
+}
+
+bool ClassFlowAnalog::doAlignAndCut(string time)
+{
+ if (disabled)
+ return true;
+
+ CAlignAndCutImage *caic = flowpostalignment->GetAlignAndCutImage();
+
+ for (int i = 0; i < ROI.size(); ++i)
+ {
+ printf("Analog %d - Align&Cut\n", i);
+
+ caic->CutAndSave(ROI[i]->posx, ROI[i]->posy, ROI[i]->deltax, ROI[i]->deltay, ROI[i]->image_org);
+ if (SaveAllFiles) ROI[i]->image_org->SaveToFile(FormatFileName("/sdcard/img_tmp/" + ROI[i]->name + ".jpg"));
+
+ ROI[i]->image_org->Resize(modelxsize, modelysize, ROI[i]->image);
+ if (SaveAllFiles) ROI[i]->image->SaveToFile(FormatFileName("/sdcard/img_tmp/" + ROI[i]->name + ".bmp"));
+ }
+
+ return true;
+}
+
+void ClassFlowAnalog::DrawROI(CImageBasis *_zw)
+{
+ int r = 0;
+ int g = 255;
+ int b = 0;
+
+ for (int i = 0; i < ROI.size(); ++i)
+ {
+ _zw->drawRect(ROI[i]->posx, ROI[i]->posy, ROI[i]->deltax, ROI[i]->deltay, r, g, b, 1);
+ _zw->drawCircle((int) (ROI[i]->posx + ROI[i]->deltax/2), (int) (ROI[i]->posy + ROI[i]->deltay/2), (int) (ROI[i]->deltax/2), r, g, b, 2);
+ _zw->drawLine((int) (ROI[i]->posx + ROI[i]->deltax/2), (int) ROI[i]->posy, (int) (ROI[i]->posx + ROI[i]->deltax/2), (int) (ROI[i]->posy + ROI[i]->deltay), r, g, b, 2);
+ _zw->drawLine((int) ROI[i]->posx, (int) (ROI[i]->posy + ROI[i]->deltay/2), (int) ROI[i]->posx + ROI[i]->deltax, (int) (ROI[i]->posy + ROI[i]->deltay/2), r, g, b, 2);
+ }
+}
+
+bool ClassFlowAnalog::doNeuralNetwork(string time)
+{
+ if (disabled)
+ return true;
+
+ string logPath = CreateLogFolder(time);
+
+ string input = "/sdcard/img_tmp/alg.jpg";
+ string ioresize = "/sdcard/img_tmp/resize.bmp";
+ string output;
+ input = FormatFileName(input);
+
+#ifndef OHNETFLITE
+ CTfLiteClass *tflite = new CTfLiteClass;
+ string zwcnn = "/sdcard" + cnnmodelfile;
+ zwcnn = FormatFileName(zwcnn);
+ printf(zwcnn.c_str());printf("\n");
+ tflite->LoadModel(zwcnn);
+ tflite->MakeAllocate();
+#endif
+
+ for (int i = 0; i < ROI.size(); ++i)
+ {
+ printf("Analog %d - TfLite\n", i);
+ ioresize = "/sdcard/img_tmp/ra" + std::to_string(i) + ".bmp";
+ ioresize = FormatFileName(ioresize);
+
+
+ float f1, f2;
+ f1 = 0; f2 = 0;
+
+#ifndef OHNETFLITE
+// LogFile.WriteToFile("ClassFlowAnalog::doNeuralNetwork vor CNN tflite->LoadInputImage(ioresize)");
+// tflite->LoadInputImage(ioresize);
+ tflite->LoadInputImageBasis(ROI[i]->image);
+ tflite->Invoke();
+ if (debugdetailanalog) LogFile.WriteToFile("Nach Invoke");
+
+
+ f1 = tflite->GetOutputValue(0);
+ f2 = tflite->GetOutputValue(1);
+#endif
+
+ float result = fmod(atan2(f1, f2) / (M_PI * 2) + 2, 1);
+// printf("Result sin, cos, ziffer: %f, %f, %f\n", f1, f2, result);
+ ROI[i]->result = result * 10;
+
+ printf("Result Analog%i: %f\n", i, ROI[i]->result);
+
+ if (isLogImage)
+ {
+ LogImage(logPath, ROI[i]->name, &ROI[i]->result, NULL, time, ROI[i]->image_org);
+ }
+ }
+#ifndef OHNETFLITE
+ delete tflite;
+#endif
+
+ return true;
+}
+
+
+std::vector ClassFlowAnalog::GetHTMLInfo()
+{
+ std::vector result;
+
+ for (int i = 0; i < ROI.size(); ++i)
+ {
+ HTMLInfo *zw = new HTMLInfo;
+ zw->filename = ROI[i]->name + ".bmp";
+ zw->filename_org = ROI[i]->name + ".jpg";
+ zw->val = ROI[i]->result;
+ zw->image = ROI[i]->image;
+ zw->image_org = ROI[i]->image_org;
+ result.push_back(zw);
+ }
+
+ return result;
+}
+
+
diff --git a/code/components/jomjol_flowcontroll/ClassFlowAnalog._h_ b/code/components/jomjol_flowcontroll/ClassFlowAnalog._h_
new file mode 100644
index 00000000..08a29be7
--- /dev/null
+++ b/code/components/jomjol_flowcontroll/ClassFlowAnalog._h_
@@ -0,0 +1,48 @@
+#pragma once
+#include "ClassFlowImage.h"
+#include "ClassFlowAlignment.h"
+// #include "CTfLiteClass.h"
+
+struct roianalog {
+ int posx, posy, deltax, deltay;
+ float result;
+ CImageBasis *image, *image_org;
+ string name;
+};
+
+
+class ClassFlowAnalog :
+ public ClassFlowImage
+{
+protected:
+ std::vector ROI;
+ string cnnmodelfile;
+ int modelxsize, modelysize;
+ int ZeigerEval(float zahl, int ziffer_vorgaenger);
+ bool SaveAllFiles;
+
+
+ ClassFlowAlignment* flowpostalignment;
+
+ void SetInitialParameter(void);
+
+public:
+ bool extendedResolution;
+
+ ClassFlowAnalog(std::vector* lfc);
+
+ bool ReadParameter(FILE* pfile, string& aktparamgraph);
+ bool doFlow(string time);
+ string getHTMLSingleStep(string host);
+ string getReadout();
+
+ void DrawROI(CImageBasis *_zw);
+
+ bool doNeuralNetwork(string time);
+ bool doAlignAndCut(string time);
+ std::vector GetHTMLInfo();
+ int AnzahlROIs();
+
+ string name(){return "ClassFlowAnalog";};
+};
+
diff --git a/code/components/jomjol_flowcontroll/ClassFlowAnalog.cpp b/code/components/jomjol_flowcontroll/ClassFlowAnalog.cpp
index a23d8b42..c38d15b5 100644
--- a/code/components/jomjol_flowcontroll/ClassFlowAnalog.cpp
+++ b/code/components/jomjol_flowcontroll/ClassFlowAnalog.cpp
@@ -1,7 +1,7 @@
#include "ClassFlowAnalog.h"
#include
-#include
+#include
#include
#include // std::stringstream
@@ -46,9 +46,9 @@ ClassFlowAnalog::ClassFlowAnalog(std::vector* lfc) : ClassFlowImage(
}
-int ClassFlowAnalog::AnzahlROIs()
+int ClassFlowAnalog::AnzahlROIs(int _analog = 0)
{
- int zw = ROI.size();
+ int zw = ANALOG[_analog]->ROI.size();
if (extendedResolution)
zw++;
@@ -56,27 +56,27 @@ int ClassFlowAnalog::AnzahlROIs()
}
-string ClassFlowAnalog::getReadout()
+string ClassFlowAnalog::getReadout(int _analog = 0)
{
string result = "";
- if (ROI.size() == 0)
+ if (ANALOG[_analog]->ROI.size() == 0)
return result;
- float zahl = ROI[ROI.size() - 1]->result;
+ float zahl = ANALOG[_analog]->ROI[ANALOG[_analog]->ROI.size() - 1]->result;
int ergebnis_nachkomma = ((int) floor(zahl * 10)) % 10;
int prev = -1;
- prev = ZeigerEval(ROI[ROI.size() - 1]->result, prev);
+ prev = ZeigerEval(ANALOG[_analog]->ROI[ANALOG[_analog]->ROI.size() - 1]->result, prev);
result = std::to_string(prev);
if (extendedResolution)
result = result + std::to_string(ergebnis_nachkomma);
- for (int i = ROI.size() - 2; i >= 0; --i)
+ for (int i = ANALOG[_analog]->ROI.size() - 2; i >= 0; --i)
{
- prev = ZeigerEval(ROI[i]->result, prev);
+ prev = ZeigerEval(ANALOG[_analog]->ROI[i]->result, prev);
result = std::to_string(prev) + result;
}
@@ -153,8 +153,8 @@ bool ClassFlowAnalog::ReadParameter(FILE* pfile, string& aktparamgraph)
}
if (zerlegt.size() >= 5)
{
- roianalog* neuroi = new roianalog;
- neuroi->name = zerlegt[0];
+ analog* _analog = GetANALOG(zerlegt[0], true);
+ roianalog* neuroi = _analog->ROI[_analog->ROI.size()-1];
neuroi->posx = std::stoi(zerlegt[1]);
neuroi->posy = std::stoi(zerlegt[2]);
neuroi->deltax = std::stoi(zerlegt[3]);
@@ -162,7 +162,7 @@ bool ClassFlowAnalog::ReadParameter(FILE* pfile, string& aktparamgraph)
neuroi->result = -1;
neuroi->image = NULL;
neuroi->image_org = NULL;
- ROI.push_back(neuroi);
+// ROI.push_back(neuroi);
}
if ((toUpper(zerlegt[0]) == "SAVEALLFILES") && (zerlegt.size() > 1))
@@ -178,15 +178,76 @@ bool ClassFlowAnalog::ReadParameter(FILE* pfile, string& aktparamgraph)
}
}
- for (int i = 0; i < ROI.size(); ++i)
- {
- ROI[i]->image = new CImageBasis(modelxsize, modelysize, 3);
- ROI[i]->image_org = new CImageBasis(ROI[i]->deltax, ROI[i]->deltay, 3);
- }
+ for (int _ana = 0; _ana < ANALOG.size(); ++_ana)
+ for (int i = 0; i < ANALOG[_ana]->ROI.size(); ++i)
+ {
+ ANALOG[_ana]->ROI[i]->image = new CImageBasis(modelxsize, modelysize, 3);
+ ANALOG[_ana]->ROI[i]->image_org = new CImageBasis(ANALOG[_ana]->ROI[i]->deltax, ANALOG[_ana]->ROI[i]->deltay, 3);
+ }
return true;
}
+analog* ClassFlowAnalog::FindANALOG(string _name_number)
+{
+ analog *_ret = NULL;
+
+ for (int i = 0; i < ANALOG.size(); ++i)
+ {
+ if (ANALOG[i]->name == _name_number)
+ return ANALOG[i];
+ }
+
+ return NULL;
+}
+
+
+
+analog* ClassFlowAnalog::GetANALOG(string _name, bool _create = true)
+{
+ string _analog, _roi;
+ int _pospunkt = _name.find_first_of(".");
+// printf("Name: %s, Pospunkt: %d\n", _name.c_str(), _pospunkt);
+ if (_pospunkt > -1)
+ {
+ _analog = _name.substr(0, _pospunkt);
+ _roi = _name.substr(_pospunkt+1, _name.length() - _pospunkt - 1);
+ }
+ else
+ {
+ _analog = "default";
+ _roi = _name;
+ }
+
+ analog *_ret = NULL;
+
+ for (int i = 0; i < ANALOG.size(); ++i)
+ {
+ if (ANALOG[i]->name == _analog)
+ _ret = ANALOG[i];
+ }
+
+ if (!_create) // nicht gefunden und soll auch nicht erzeugt werden
+ return _ret;
+
+
+ if (_ret == NULL)
+ {
+ _ret = new analog;
+ _ret->name = _analog;
+ ANALOG.push_back(_ret);
+ }
+
+ roianalog* neuroi = new roianalog;
+ neuroi->name = _roi;
+ _ret->ROI.push_back(neuroi);
+
+ printf("GetANALOG - ANALOG %s - roi %s\n", _analog.c_str(), _roi.c_str());
+
+ return _ret;
+}
+
+
string ClassFlowAnalog::getHTMLSingleStep(string host)
{
@@ -238,16 +299,29 @@ bool ClassFlowAnalog::doAlignAndCut(string time)
CAlignAndCutImage *caic = flowpostalignment->GetAlignAndCutImage();
- for (int i = 0; i < ROI.size(); ++i)
- {
- printf("Analog %d - Align&Cut\n", i);
-
- caic->CutAndSave(ROI[i]->posx, ROI[i]->posy, ROI[i]->deltax, ROI[i]->deltay, ROI[i]->image_org);
- if (SaveAllFiles) ROI[i]->image_org->SaveToFile(FormatFileName("/sdcard/img_tmp/" + ROI[i]->name + ".jpg"));
+ for (int _ana = 0; _ana < ANALOG.size(); ++_ana)
+ for (int i = 0; i < ANALOG[_ana]->ROI.size(); ++i)
+ {
+ printf("Analog %d - Align&Cut\n", i);
+
+ caic->CutAndSave(ANALOG[_ana]->ROI[i]->posx, ANALOG[_ana]->ROI[i]->posy, ANALOG[_ana]->ROI[i]->deltax, ANALOG[_ana]->ROI[i]->deltay, ANALOG[_ana]->ROI[i]->image_org);
+ if (SaveAllFiles)
+ {
+ if (ANALOG[_ana]->name == "default")
+ ANALOG[_ana]->ROI[i]->image_org->SaveToFile(FormatFileName("/sdcard/img_tmp/" + ANALOG[_ana]->ROI[i]->name + ".jpg"));
+ else
+ ANALOG[_ana]->ROI[i]->image_org->SaveToFile(FormatFileName("/sdcard/img_tmp/" + ANALOG[_ana]->name + "_" + ANALOG[_ana]->ROI[i]->name + ".jpg"));
+ }
- ROI[i]->image_org->Resize(modelxsize, modelysize, ROI[i]->image);
- if (SaveAllFiles) ROI[i]->image->SaveToFile(FormatFileName("/sdcard/img_tmp/" + ROI[i]->name + ".bmp"));
- }
+ ANALOG[_ana]->ROI[i]->image_org->Resize(modelxsize, modelysize, ANALOG[_ana]->ROI[i]->image);
+ if (SaveAllFiles)
+ {
+ if (ANALOG[_ana]->name == "default")
+ ANALOG[_ana]->ROI[i]->image->SaveToFile(FormatFileName("/sdcard/img_tmp/" + ANALOG[_ana]->ROI[i]->name + ".bmp"));
+ else
+ ANALOG[_ana]->ROI[i]->image->SaveToFile(FormatFileName("/sdcard/img_tmp/" + ANALOG[_ana]->name + "_" + ANALOG[_ana]->ROI[i]->name + ".bmp"));
+ }
+ }
return true;
}
@@ -258,13 +332,14 @@ void ClassFlowAnalog::DrawROI(CImageBasis *_zw)
int g = 255;
int b = 0;
- for (int i = 0; i < ROI.size(); ++i)
- {
- _zw->drawRect(ROI[i]->posx, ROI[i]->posy, ROI[i]->deltax, ROI[i]->deltay, r, g, b, 1);
- _zw->drawCircle((int) (ROI[i]->posx + ROI[i]->deltax/2), (int) (ROI[i]->posy + ROI[i]->deltay/2), (int) (ROI[i]->deltax/2), r, g, b, 2);
- _zw->drawLine((int) (ROI[i]->posx + ROI[i]->deltax/2), (int) ROI[i]->posy, (int) (ROI[i]->posx + ROI[i]->deltax/2), (int) (ROI[i]->posy + ROI[i]->deltay), r, g, b, 2);
- _zw->drawLine((int) ROI[i]->posx, (int) (ROI[i]->posy + ROI[i]->deltay/2), (int) ROI[i]->posx + ROI[i]->deltax, (int) (ROI[i]->posy + ROI[i]->deltay/2), r, g, b, 2);
- }
+ for (int _ana = 0; _ana < ANALOG.size(); ++_ana)
+ for (int i = 0; i < ANALOG[_ana]->ROI.size(); ++i)
+ {
+ _zw->drawRect(ANALOG[_ana]->ROI[i]->posx, ANALOG[_ana]->ROI[i]->posy, ANALOG[_ana]->ROI[i]->deltax, ANALOG[_ana]->ROI[i]->deltay, r, g, b, 1);
+ _zw->drawCircle((int) (ANALOG[_ana]->ROI[i]->posx + ANALOG[_ana]->ROI[i]->deltax/2), (int) (ANALOG[_ana]->ROI[i]->posy + ANALOG[_ana]->ROI[i]->deltay/2), (int) (ANALOG[_ana]->ROI[i]->deltax/2), r, g, b, 2);
+ _zw->drawLine((int) (ANALOG[_ana]->ROI[i]->posx + ANALOG[_ana]->ROI[i]->deltax/2), (int) ANALOG[_ana]->ROI[i]->posy, (int) (ANALOG[_ana]->ROI[i]->posx + ANALOG[_ana]->ROI[i]->deltax/2), (int) (ANALOG[_ana]->ROI[i]->posy + ANALOG[_ana]->ROI[i]->deltay), r, g, b, 2);
+ _zw->drawLine((int) ANALOG[_ana]->ROI[i]->posx, (int) (ANALOG[_ana]->ROI[i]->posy + ANALOG[_ana]->ROI[i]->deltay/2), (int) ANALOG[_ana]->ROI[i]->posx + ANALOG[_ana]->ROI[i]->deltax, (int) (ANALOG[_ana]->ROI[i]->posy + ANALOG[_ana]->ROI[i]->deltay/2), r, g, b, 2);
+ }
}
bool ClassFlowAnalog::doNeuralNetwork(string time)
@@ -288,39 +363,38 @@ bool ClassFlowAnalog::doNeuralNetwork(string time)
tflite->MakeAllocate();
#endif
- for (int i = 0; i < ROI.size(); ++i)
+ for (int _ana = 0; _ana < ANALOG.size(); ++_ana)
{
- printf("Analog %d - TfLite\n", i);
- ioresize = "/sdcard/img_tmp/ra" + std::to_string(i) + ".bmp";
- ioresize = FormatFileName(ioresize);
-
-
- float f1, f2;
- f1 = 0; f2 = 0;
-
-#ifndef OHNETFLITE
-// LogFile.WriteToFile("ClassFlowAnalog::doNeuralNetwork vor CNN tflite->LoadInputImage(ioresize)");
-// tflite->LoadInputImage(ioresize);
- tflite->LoadInputImageBasis(ROI[i]->image);
- tflite->Invoke();
- if (debugdetailanalog) LogFile.WriteToFile("Nach Invoke");
-
-
- f1 = tflite->GetOutputValue(0);
- f2 = tflite->GetOutputValue(1);
-#endif
-
- float result = fmod(atan2(f1, f2) / (M_PI * 2) + 2, 1);
-// printf("Result sin, cos, ziffer: %f, %f, %f\n", f1, f2, result);
- ROI[i]->result = result * 10;
-
- printf("Result Analog%i: %f\n", i, ROI[i]->result);
-
- if (isLogImage)
+ for (int i = 0; i < ANALOG[_ana]->ROI.size(); ++i)
{
- LogImage(logPath, ROI[i]->name, &ROI[i]->result, NULL, time, ROI[i]->image_org);
+ printf("Analog %d - TfLite\n", i);
+
+ float f1, f2;
+ f1 = 0; f2 = 0;
+
+ #ifndef OHNETFLITE
+ tflite->LoadInputImageBasis(ANALOG[_ana]->ROI[i]->image);
+ tflite->Invoke();
+ if (debugdetailanalog) LogFile.WriteToFile("Nach Invoke");
+
+
+ f1 = tflite->GetOutputValue(0);
+ f2 = tflite->GetOutputValue(1);
+ #endif
+
+ float result = fmod(atan2(f1, f2) / (M_PI * 2) + 2, 1);
+ // printf("Result sin, cos, ziffer: %f, %f, %f\n", f1, f2, result);
+ ANALOG[_ana]->ROI[i]->result = result * 10;
+
+ printf("Result Analog%i: %f\n", i, ANALOG[_ana]->ROI[i]->result);
+
+ if (isLogImage)
+ {
+ LogImage(logPath, ANALOG[_ana]->ROI[i]->name, &ANALOG[_ana]->ROI[i]->result, NULL, time, ANALOG[_ana]->ROI[i]->image_org);
+ }
}
}
+
#ifndef OHNETFLITE
delete tflite;
#endif
@@ -333,18 +407,78 @@ std::vector ClassFlowAnalog::GetHTMLInfo()
{
std::vector result;
- for (int i = 0; i < ROI.size(); ++i)
- {
- HTMLInfo *zw = new HTMLInfo;
- zw->filename = ROI[i]->name + ".bmp";
- zw->filename_org = ROI[i]->name + ".jpg";
- zw->val = ROI[i]->result;
- zw->image = ROI[i]->image;
- zw->image_org = ROI[i]->image_org;
- result.push_back(zw);
- }
+ for (int _ana = 0; _ana < ANALOG.size(); ++_ana)
+ for (int i = 0; i < ANALOG[_ana]->ROI.size(); ++i)
+ {
+ if (ANALOG[_ana]->name == "default")
+ ANALOG[_ana]->ROI[i]->image->SaveToFile(FormatFileName("/sdcard/img_tmp/" + ANALOG[_ana]->ROI[i]->name + ".bmp"));
+ else
+ ANALOG[_ana]->ROI[i]->image->SaveToFile(FormatFileName("/sdcard/img_tmp/" + ANALOG[_ana]->name + "_" + ANALOG[_ana]->ROI[i]->name + ".bmp"));
+
+
+ HTMLInfo *zw = new HTMLInfo;
+ if (ANALOG[_ana]->name == "default")
+ {
+ zw->filename = ANALOG[_ana]->ROI[i]->name + ".bmp";
+ zw->filename_org = ANALOG[_ana]->ROI[i]->name + ".jpg";
+ }
+ else
+ {
+ zw->filename = ANALOG[_ana]->name + "_" + ANALOG[_ana]->ROI[i]->name + ".bmp";
+ zw->filename_org = ANALOG[_ana]->name + "_" + ANALOG[_ana]->ROI[i]->name + ".jpg";
+ }
+
+ zw->val = ANALOG[_ana]->ROI[i]->result;
+ zw->image = ANALOG[_ana]->ROI[i]->image;
+ zw->image_org = ANALOG[_ana]->ROI[i]->image_org;
+
+ result.push_back(zw);
+ }
return result;
}
+
+int ClassFlowAnalog::getAnzahlANALOG()
+{
+ return ANALOG.size();
+}
+
+string ClassFlowAnalog::getNameANALOG(int _analog)
+{
+ if (_analog < ANALOG.size())
+ return ANALOG[_analog]->name;
+
+ return "ANALOG DOES NOT EXIST";
+}
+
+analog* ClassFlowAnalog::GetANALOG(int _analog)
+{
+ if (_analog < ANALOG.size())
+ return ANALOG[_analog];
+
+ return NULL;
+}
+
+
+
+void ClassFlowAnalog::UpdateNameNumbers(std::vector *_name_numbers)
+{
+ for (int _dig = 0; _dig < ANALOG.size(); _dig++)
+ {
+ std::string _name = ANALOG[_dig]->name;
+ bool found = false;
+ for (int i = 0; i < (*_name_numbers).size(); ++i)
+ {
+ if ((*_name_numbers)[i] == _name)
+ found = true;
+ }
+ if (!found)
+ (*_name_numbers).push_back(_name);
+ }
+}
+
+
+
+
diff --git a/code/components/jomjol_flowcontroll/ClassFlowAnalog.h b/code/components/jomjol_flowcontroll/ClassFlowAnalog.h
index 08a29be7..bd9af089 100644
--- a/code/components/jomjol_flowcontroll/ClassFlowAnalog.h
+++ b/code/components/jomjol_flowcontroll/ClassFlowAnalog.h
@@ -10,12 +10,19 @@ struct roianalog {
string name;
};
+struct analog {
+ string name;
+ std::vector ROI;
+};
+
class ClassFlowAnalog :
public ClassFlowImage
{
protected:
- std::vector ROI;
+// std::vector ROI;
+ std::vector ANALOG;
+
string cnnmodelfile;
int modelxsize, modelysize;
int ZeigerEval(float zahl, int ziffer_vorgaenger);
@@ -24,7 +31,8 @@ protected:
ClassFlowAlignment* flowpostalignment;
- void SetInitialParameter(void);
+ void SetInitialParameter(void);
+
public:
bool extendedResolution;
@@ -34,14 +42,23 @@ public:
bool ReadParameter(FILE* pfile, string& aktparamgraph);
bool doFlow(string time);
string getHTMLSingleStep(string host);
- string getReadout();
+ string getReadout(int _analog);
void DrawROI(CImageBasis *_zw);
bool doNeuralNetwork(string time);
bool doAlignAndCut(string time);
std::vector GetHTMLInfo();
- int AnzahlROIs();
+ int AnzahlROIs(int _analog);
+
+ int getAnzahlANALOG();
+ analog* GetANALOG(int _analog);
+ analog* GetANALOG(string _name, bool _create);
+ analog* FindANALOG(string _name_number);
+ string getNameANALOG(int _analog);
+
+ void UpdateNameNumbers(std::vector *_name_numbers);
+
string name(){return "ClassFlowAnalog";};
};
diff --git a/code/components/jomjol_flowcontroll/ClassFlowControll.cpp b/code/components/jomjol_flowcontroll/ClassFlowControll.cpp
index 7399aa23..921e40b0 100644
--- a/code/components/jomjol_flowcontroll/ClassFlowControll.cpp
+++ b/code/components/jomjol_flowcontroll/ClassFlowControll.cpp
@@ -262,6 +262,38 @@ void ClassFlowControll::UpdateAktStatus(std::string _flow)
}
+string ClassFlowControll::getReadoutAll(int _type)
+{
+ std::vector numbers = flowpostprocessing->GetNumbers();
+ std::string out = "";
+
+ for (int i = 0; i < numbers.size(); ++i)
+ {
+ out = out + numbers[i]->name + "\t";
+ switch (_type) {
+ case READOUT_TYPE_VALUE:
+ out = out + numbers[i]->ReturnValue;
+ break;
+ case READOUT_TYPE_PREVALUE:
+ out = out + std::to_string(numbers[i]->PreValue);
+ break;
+ case READOUT_TYPE_RAWVALUE:
+ out = out + numbers[i]->ReturnRawValue;
+ break;
+ case READOUT_TYPE_ERROR:
+ out = out + numbers[i]->ErrorMessageText;
+ break;
+ }
+ if (i < numbers.size()-1)
+ out = out + "\r\n";
+ }
+
+// printf("OUT: %s", out.c_str());
+
+ return out;
+}
+
+
string ClassFlowControll::getReadout(bool _rawvalue = false, bool _noerror = false)
{
if (flowpostprocessing)
@@ -285,17 +317,17 @@ string ClassFlowControll::getReadout(bool _rawvalue = false, bool _noerror = fal
return result;
}
-string ClassFlowControll::GetPrevalue()
+string ClassFlowControll::GetPrevalue(std::string _number)
{
if (flowpostprocessing)
{
- return flowpostprocessing->GetPreValue();
+ return flowpostprocessing->GetPreValue(_number);
}
return std::string();
}
-std::string ClassFlowControll::UpdatePrevalue(std::string _newvalue)
+std::string ClassFlowControll::UpdatePrevalue(std::string _newvalue, std::string _numbers)
{
float zw;
char* p;
@@ -317,7 +349,7 @@ std::string ClassFlowControll::UpdatePrevalue(std::string _newvalue)
if (flowpostprocessing)
{
- flowpostprocessing->SavePreValue(zw);
+ flowpostprocessing->SetPreValue(zw, _numbers);
return _newvalue;
}
diff --git a/code/components/jomjol_flowcontroll/ClassFlowControll.h b/code/components/jomjol_flowcontroll/ClassFlowControll.h
index 6ef26a2c..7a21406b 100644
--- a/code/components/jomjol_flowcontroll/ClassFlowControll.h
+++ b/code/components/jomjol_flowcontroll/ClassFlowControll.h
@@ -11,6 +11,12 @@
#include "ClassFlowMQTT.h"
+#define READOUT_TYPE_VALUE 0
+#define READOUT_TYPE_PREVALUE 1
+#define READOUT_TYPE_RAWVALUE 2
+#define READOUT_TYPE_ERROR 3
+
+
class ClassFlowControll :
public ClassFlow
{
@@ -38,8 +44,9 @@ public:
void doFlowMakeImageOnly(string time);
bool getStatusSetupModus(){return SetupModeActive;};
string getReadout(bool _rawvalue, bool _noerror);
- string UpdatePrevalue(std::string _newvalue);
- string GetPrevalue();
+ string getReadoutAll(int _type);
+ string UpdatePrevalue(std::string _newvalue, std::string _numbers);
+ string GetPrevalue(std::string _number = "");
bool ReadParameter(FILE* pfile, string& aktparamgraph);
esp_err_t GetJPGStream(std::string _fn, httpd_req_t *req);
diff --git a/code/components/jomjol_flowcontroll/ClassFlowDigit.cpp b/code/components/jomjol_flowcontroll/ClassFlowDigit.cpp
index 6e37cc0b..dad5fcfe 100644
--- a/code/components/jomjol_flowcontroll/ClassFlowDigit.cpp
+++ b/code/components/jomjol_flowcontroll/ClassFlowDigit.cpp
@@ -64,16 +64,16 @@ ClassFlowDigit::ClassFlowDigit(std::vector* lfc, ClassFlow *_prev) :
}
}
-string ClassFlowDigit::getReadout()
+string ClassFlowDigit::getReadout(int _digit = 0)
{
string rst = "";
- for (int i = 0; i < ROI.size(); ++i)
+ for (int i = 0; i < DIGIT[_digit]->ROI.size(); ++i)
{
- if (ROI[i]->resultklasse == 10)
+ if (DIGIT[_digit]->ROI[i]->resultklasse == 10)
rst = rst + "N";
else
- rst = rst + std::to_string(ROI[i]->resultklasse);
+ rst = rst + std::to_string(DIGIT[_digit]->ROI[i]->resultklasse);
}
return rst;
@@ -91,18 +91,11 @@ bool ClassFlowDigit::ReadParameter(FILE* pfile, string& aktparamgraph)
printf("aktparamgraph: %s\n", aktparamgraph.c_str());
-
-/*
- if ((aktparamgraph.compare("[Digits]") != 0) && (aktparamgraph.compare(";[Digits]") != 0)) // Paragraph passt nich zu MakeImage
- return false;
-*/
-
if ((aktparamgraph.compare(0, 7, "[Digits") != 0) && (aktparamgraph.compare(0, 8, ";[Digits") != 0)) // Paragraph passt nich zu MakeImage
return false;
int _pospkt = aktparamgraph.find_first_of(".");
int _posklammerzu = aktparamgraph.find_first_of("]");
-// printf("Pos: %d, %d\n", _pospkt, _posklammerzu);
if (_pospkt > -1)
NameDigit = aktparamgraph.substr(_pospkt+1, _posklammerzu - _pospkt-1);
else
@@ -137,8 +130,8 @@ bool ClassFlowDigit::ReadParameter(FILE* pfile, string& aktparamgraph)
}
if (zerlegt.size() >= 5)
{
- roi* neuroi = new roi;
- neuroi->name = zerlegt[0];
+ digit* _digit = GetDIGIT(zerlegt[0], true);
+ roi* neuroi = _digit->ROI[_digit->ROI.size()-1];
neuroi->posx = std::stoi(zerlegt[1]);
neuroi->posy = std::stoi(zerlegt[2]);
neuroi->deltax = std::stoi(zerlegt[3]);
@@ -146,7 +139,6 @@ bool ClassFlowDigit::ReadParameter(FILE* pfile, string& aktparamgraph)
neuroi->resultklasse = -1;
neuroi->image = NULL;
neuroi->image_org = NULL;
- ROI.push_back(neuroi);
}
if ((toUpper(zerlegt[0]) == "SAVEALLFILES") && (zerlegt.size() > 1))
@@ -157,15 +149,74 @@ bool ClassFlowDigit::ReadParameter(FILE* pfile, string& aktparamgraph)
}
- for (int i = 0; i < ROI.size(); ++i)
- {
- ROI[i]->image = new CImageBasis(modelxsize, modelysize, 3);
- ROI[i]->image_org = new CImageBasis(ROI[i]->deltax, ROI[i]->deltay, 3);
- }
+ for (int _dig = 0; _dig < DIGIT.size(); ++_dig)
+ for (int i = 0; i < DIGIT[_dig]->ROI.size(); ++i)
+ {
+ DIGIT[_dig]->ROI[i]->image = new CImageBasis(modelxsize, modelysize, 3);
+ DIGIT[_dig]->ROI[i]->image_org = new CImageBasis(DIGIT[_dig]->ROI[i]->deltax, DIGIT[_dig]->ROI[i]->deltay, 3);
+ }
return true;
}
+digit* ClassFlowDigit::FindDIGIT(string _name_number)
+{
+ digit *_ret = NULL;
+
+ for (int i = 0; i < DIGIT.size(); ++i)
+ {
+ if (DIGIT[i]->name == _name_number)
+ return DIGIT[i];
+ }
+
+ return NULL;
+}
+
+
+digit* ClassFlowDigit::GetDIGIT(string _name, bool _create = true)
+{
+ string _digit, _roi;
+ int _pospunkt = _name.find_first_of(".");
+// printf("Name: %s, Pospunkt: %d\n", _name.c_str(), _pospunkt);
+ if (_pospunkt > -1)
+ {
+ _digit = _name.substr(0, _pospunkt);
+ _roi = _name.substr(_pospunkt+1, _name.length() - _pospunkt - 1);
+ }
+ else
+ {
+ _digit = "default";
+ _roi = _name;
+ }
+
+ digit *_ret = NULL;
+
+ for (int i = 0; i < DIGIT.size(); ++i)
+ {
+ if (DIGIT[i]->name == _digit)
+ _ret = DIGIT[i];
+ }
+
+ if (!_create) // nicht gefunden und soll auch nicht erzeugt werden, ggf. geht eine NULL zurück
+ return _ret;
+
+ if (_ret == NULL)
+ {
+ _ret = new digit;
+ _ret->name = _digit;
+ DIGIT.push_back(_ret);
+ }
+
+ roi* neuroi = new roi;
+ neuroi->name = _roi;
+ _ret->ROI.push_back(neuroi);
+
+ printf("GetDIGIT - digit %s - roi %s\n", _digit.c_str(), _roi.c_str());
+
+ return _ret;
+}
+
+
string ClassFlowDigit::getHTMLSingleStep(string host)
{
@@ -216,17 +267,32 @@ bool ClassFlowDigit::doAlignAndCut(string time)
CAlignAndCutImage *caic = flowpostalignment->GetAlignAndCutImage();
- for (int i = 0; i < ROI.size(); ++i)
+ for (int _dig = 0; _dig < DIGIT.size(); ++_dig)
{
- printf("DigitalDigit %d - Align&Cut\n", i);
-
- caic->CutAndSave(ROI[i]->posx, ROI[i]->posy, ROI[i]->deltax, ROI[i]->deltay, ROI[i]->image_org);
- if (SaveAllFiles) ROI[i]->image_org->SaveToFile(FormatFileName("/sdcard/img_tmp/" + ROI[i]->name + ".jpg"));
+ printf("DIGIT[_dig]->ROI.size() %d\n", DIGIT[_dig]->ROI.size());
+ for (int i = 0; i < DIGIT[_dig]->ROI.size(); ++i)
+ {
+ printf("DigitalDigit %d - Align&Cut\n", i);
+
+ caic->CutAndSave(DIGIT[_dig]->ROI[i]->posx, DIGIT[_dig]->ROI[i]->posy, DIGIT[_dig]->ROI[i]->deltax, DIGIT[_dig]->ROI[i]->deltay, DIGIT[_dig]->ROI[i]->image_org);
+ if (SaveAllFiles)
+ {
+ if (DIGIT[_dig]->name == "default")
+ DIGIT[_dig]->ROI[i]->image_org->SaveToFile(FormatFileName("/sdcard/img_tmp/" + DIGIT[_dig]->ROI[i]->name + ".jpg"));
+ else
+ DIGIT[_dig]->ROI[i]->image_org->SaveToFile(FormatFileName("/sdcard/img_tmp/" + DIGIT[_dig]->name + "_" + DIGIT[_dig]->ROI[i]->name + ".jpg"));
+ }
- ROI[i]->image_org->Resize(modelxsize, modelysize, ROI[i]->image);
- if (SaveAllFiles) ROI[i]->image->SaveToFile(FormatFileName("/sdcard/img_tmp/" + ROI[i]->name + ".bmp"));
+ DIGIT[_dig]->ROI[i]->image_org->Resize(modelxsize, modelysize, DIGIT[_dig]->ROI[i]->image);
+ if (SaveAllFiles)
+ {
+ if (DIGIT[_dig]->name == "default")
+ DIGIT[_dig]->ROI[i]->image->SaveToFile(FormatFileName("/sdcard/img_tmp/" + DIGIT[_dig]->ROI[i]->name + ".bmp"));
+ else
+ DIGIT[_dig]->ROI[i]->image->SaveToFile(FormatFileName("/sdcard/img_tmp/" + DIGIT[_dig]->name + "_" + DIGIT[_dig]->ROI[i]->name + ".bmp"));
+ }
+ }
}
-
return true;
}
@@ -245,22 +311,23 @@ bool ClassFlowDigit::doNeuralNetwork(string time)
tflite->MakeAllocate();
#endif
- for (int i = 0; i < ROI.size(); ++i)
- {
- printf("DigitalDigit %d - TfLite\n", i);
-
- ROI[i]->resultklasse = 0;
-#ifndef OHNETFLITE
- ROI[i]->resultklasse = tflite->GetClassFromImageBasis(ROI[i]->image);
-
-#endif
- printf("Result Digit%i: %d\n", i, ROI[i]->resultklasse);
-
- if (isLogImage)
+ for (int _dig = 0; _dig < DIGIT.size(); ++_dig)
+ for (int i = 0; i < DIGIT[_dig]->ROI.size(); ++i)
{
- LogImage(logPath, ROI[i]->name, NULL, &ROI[i]->resultklasse, time, ROI[i]->image_org);
+ printf("DigitalDigit %d - TfLite\n", i);
+
+ DIGIT[_dig]->ROI[i]->resultklasse = 0;
+ #ifndef OHNETFLITE
+ DIGIT[_dig]->ROI[i]->resultklasse = tflite->GetClassFromImageBasis(DIGIT[_dig]->ROI[i]->image);
+
+ #endif
+ printf("Result Digit%i: %d\n", i, DIGIT[_dig]->ROI[i]->resultklasse);
+
+ if (isLogImage)
+ {
+ LogImage(logPath, DIGIT[_dig]->ROI[i]->name, NULL, &DIGIT[_dig]->ROI[i]->resultklasse, time, DIGIT[_dig]->ROI[i]->image_org);
+ }
}
- }
#ifndef OHNETFLITE
delete tflite;
#endif
@@ -269,25 +336,82 @@ bool ClassFlowDigit::doNeuralNetwork(string time)
void ClassFlowDigit::DrawROI(CImageBasis *_zw)
{
- for (int i = 0; i < ROI.size(); ++i)
- _zw->drawRect(ROI[i]->posx, ROI[i]->posy, ROI[i]->deltax, ROI[i]->deltay, 0, 0, 255, 2);
+ for (int _dig = 0; _dig < DIGIT.size(); ++_dig)
+ for (int i = 0; i < DIGIT[_dig]->ROI.size(); ++i)
+ _zw->drawRect(DIGIT[_dig]->ROI[i]->posx, DIGIT[_dig]->ROI[i]->posy, DIGIT[_dig]->ROI[i]->deltax, DIGIT[_dig]->ROI[i]->deltay, 0, 0, (255 - _dig*100), 2);
}
std::vector ClassFlowDigit::GetHTMLInfo()
{
std::vector result;
- for (int i = 0; i < ROI.size(); ++i)
- {
- HTMLInfo *zw = new HTMLInfo;
- zw->filename = ROI[i]->name + ".bmp";
- zw->filename_org = ROI[i]->name + ".jpg";
- zw->val = ROI[i]->resultklasse;
- zw->image = ROI[i]->image;
- zw->image_org = ROI[i]->image_org;
- result.push_back(zw);
- }
+ for (int _dig = 0; _dig < DIGIT.size(); ++_dig)
+ for (int i = 0; i < DIGIT[_dig]->ROI.size(); ++i)
+ {
+ if (DIGIT[_dig]->name == "default")
+ DIGIT[_dig]->ROI[i]->image->SaveToFile(FormatFileName("/sdcard/img_tmp/" + DIGIT[_dig]->ROI[i]->name + ".bmp"));
+ else
+ DIGIT[_dig]->ROI[i]->image->SaveToFile(FormatFileName("/sdcard/img_tmp/" + DIGIT[_dig]->name + "_" + DIGIT[_dig]->ROI[i]->name + ".bmp"));
+
+
+ HTMLInfo *zw = new HTMLInfo;
+ if (DIGIT[_dig]->name == "default")
+ {
+ zw->filename = DIGIT[_dig]->ROI[i]->name + ".bmp";
+ zw->filename_org = DIGIT[_dig]->ROI[i]->name + ".jpg";
+ }
+ else
+ {
+ zw->filename = DIGIT[_dig]->name + "_" + DIGIT[_dig]->ROI[i]->name + ".bmp";
+ zw->filename_org = DIGIT[_dig]->name + "_" + DIGIT[_dig]->ROI[i]->name + ".jpg";
+ }
+
+ zw->val = DIGIT[_dig]->ROI[i]->resultklasse;
+ zw->image = DIGIT[_dig]->ROI[i]->image;
+ zw->image_org = DIGIT[_dig]->ROI[i]->image_org;
+ result.push_back(zw);
+ }
return result;
}
+int ClassFlowDigit::getAnzahlDIGIT()
+{
+ return DIGIT.size();
+}
+
+string ClassFlowDigit::getNameDIGIT(int _digit)
+{
+ if (_digit < DIGIT.size())
+ return DIGIT[_digit]->name;
+
+ return "DIGIT DOES NOT EXIST";
+}
+
+digit* ClassFlowDigit::GetDIGIT(int _digit)
+{
+ if (_digit < DIGIT.size())
+ return DIGIT[_digit];
+
+ return NULL;
+}
+
+void ClassFlowDigit::UpdateNameNumbers(std::vector *_name_numbers)
+{
+ for (int _dig = 0; _dig < DIGIT.size(); _dig++)
+ {
+ std::string _name = DIGIT[_dig]->name;
+ bool found = false;
+ for (int i = 0; i < (*_name_numbers).size(); ++i)
+ {
+ if ((*_name_numbers)[i] == _name)
+ found = true;
+ }
+ if (!found)
+ (*_name_numbers).push_back(_name);
+ }
+}
+
+
+
+
diff --git a/code/components/jomjol_flowcontroll/ClassFlowDigit.h b/code/components/jomjol_flowcontroll/ClassFlowDigit.h
index 3af92f85..f6d98616 100644
--- a/code/components/jomjol_flowcontroll/ClassFlowDigit.h
+++ b/code/components/jomjol_flowcontroll/ClassFlowDigit.h
@@ -5,6 +5,8 @@
#include
+
+
struct roi {
int posx, posy, deltax, deltay;
int resultklasse;
@@ -13,11 +15,17 @@ struct roi {
roi* next;
};
+struct digit {
+ string name;
+ std::vector ROI;
+};
+
class ClassFlowDigit :
public ClassFlowImage
{
protected:
- std::vector ROI;
+// std::vector ROI;
+ std::vector DIGIT;
string cnnmodelfile;
int modelxsize, modelysize;
bool SaveAllFiles;
@@ -31,6 +39,7 @@ protected:
bool doNeuralNetwork(string time);
bool doAlignAndCut(string time);
+
void SetInitialParameter(void);
public:
@@ -40,9 +49,18 @@ public:
bool ReadParameter(FILE* pfile, string& aktparamgraph);
bool doFlow(string time);
string getHTMLSingleStep(string host);
- string getReadout();
+ string getReadout(int _digit);
std::vector GetHTMLInfo();
+ int getAnzahlDIGIT();
+ digit* GetDIGIT(int _digit);
+ digit* GetDIGIT(string _name, bool _create);
+ digit* FindDIGIT(string _name_number);
+
+ string getNameDIGIT(int _digit);
+
+ void UpdateNameNumbers(std::vector *_name_numbers);
+
void DrawROI(CImageBasis *_zw);
string name(){return "ClassFlowDigit";};
diff --git a/code/components/jomjol_flowcontroll/ClassFlowPostProcessing.cpp b/code/components/jomjol_flowcontroll/ClassFlowPostProcessing.cpp
index e9d54cb0..25c8c684 100644
--- a/code/components/jomjol_flowcontroll/ClassFlowPostProcessing.cpp
+++ b/code/components/jomjol_flowcontroll/ClassFlowPostProcessing.cpp
@@ -1,8 +1,6 @@
#include "ClassFlowPostProcessing.h"
#include "Helper.h"
-#include "ClassFlowAnalog.h"
-#include "ClassFlowDigit.h"
#include "ClassFlowMakeImage.h"
#include "ClassLogFile.h"
@@ -18,130 +16,203 @@
#define PREVALUE_TIME_FORMAT_INPUT "%d-%d-%dT%d:%d:%d"
-string ClassFlowPostProcessing::GetPreValue()
+string ClassFlowPostProcessing::GetPreValue(std::string _number)
{
std::string result;
- bool isAnalog = false;
- bool isDigit = false;
+ int index = -1;
- int AnzahlAnalog = 0;
- result = RundeOutput(PreValue, -DecimalShift);
+ if (_number == "")
+ _number = "default";
- for (int i = 0; i < ListFlowControll->size(); ++i)
- {
- if (((*ListFlowControll)[i])->name().compare("ClassFlowAnalog") == 0)
- {
- isAnalog = true;
- AnzahlAnalog = ((ClassFlowAnalog*)(*ListFlowControll)[i])->AnzahlROIs();
- }
- if (((*ListFlowControll)[i])->name().compare("ClassFlowDigit") == 0)
- {
- isDigit = true;
- }
- }
+ for (int i = 0; i < NUMBERS.size(); ++i)
+ if (NUMBERS[i]->name == _number)
+ index = i;
- if (isDigit && isAnalog)
- result = RundeOutput(PreValue, AnzahlAnalog - DecimalShift);
+ result = RundeOutput(NUMBERS[index]->PreValue, -NUMBERS[index]->DecimalShift);
+
+ if (NUMBERS[index]->digit_roi && NUMBERS[index]->analog_roi)
+ result = RundeOutput(NUMBERS[index]->PreValue, NUMBERS[index]->AnzahlAnalog - NUMBERS[index]->DecimalShift);
return result;
}
+void ClassFlowPostProcessing::SetPreValue(float zw, string _numbers)
+{
+ for (int j = 0; j < NUMBERS.size(); ++j)
+ {
+ if (NUMBERS[j]->name == _numbers)
+ NUMBERS[j]->PreValue = zw;
+ }
+ UpdatePreValueINI = true;
+ SavePreValue();
+}
+
+
bool ClassFlowPostProcessing::LoadPreValue(void)
{
+ std::vector zerlegt;
FILE* pFile;
char zw[1024];
- string zwtime, zwvalue;
+ string zwtime, zwvalue, name;
+ bool _done = false;
+
+ UpdatePreValueINI = false; // Konvertierung ins neue Format
+
pFile = fopen(FilePreValue.c_str(), "r");
if (pFile == NULL)
return false;
fgets(zw, 1024, pFile);
- printf("%s", zw);
+ printf("Read Zeile Prevalue.ini: %s", zw);
zwtime = trim(std::string(zw));
-
- fgets(zw, 1024, pFile);
- fclose(pFile);
- printf("%s", zw);
- zwvalue = trim(std::string(zw));
- PreValue = stof(zwvalue.c_str());
-
- time_t tStart;
- int yy, month, dd, hh, mm, ss;
- struct tm whenStart;
-
- sscanf(zwtime.c_str(), PREVALUE_TIME_FORMAT_INPUT, &yy, &month, &dd, &hh, &mm, &ss);
- whenStart.tm_year = yy - 1900;
- whenStart.tm_mon = month - 1;
- whenStart.tm_mday = dd;
- whenStart.tm_hour = hh;
- whenStart.tm_min = mm;
- whenStart.tm_sec = ss;
- whenStart.tm_isdst = -1;
-
- lastvalue = mktime(&whenStart);
-
- time(&tStart);
- localtime(&tStart);
- double difference = difftime(tStart, lastvalue);
- difference /= 60;
- if (difference > PreValueAgeStartup)
+ if (zwtime.length() == 0)
return false;
- Value = PreValue;
- ReturnValue = to_string(Value);
- ReturnValueNoError = ReturnValue;
-
- bool isAnalog = false;
- bool isDigit = false;
- int AnzahlAnalog = 0;
-
- for (int i = 0; i < ListFlowControll->size(); ++i)
+ zerlegt = HelperZerlegeZeile(zwtime, "\t");
+ if (zerlegt.size() > 1) // neues Format
{
- if (((*ListFlowControll)[i])->name().compare("ClassFlowAnalog") == 0)
- isAnalog = true;
- if (((*ListFlowControll)[i])->name().compare("ClassFlowDigit") == 0)
- isDigit = true;
- }
+ while ((zerlegt.size() > 1) && !_done)
+ {
+ name = trim(zerlegt[0]);
+ zwtime = trim(zerlegt[1]);
+ zwvalue = trim(zerlegt[2]);
- if (isDigit || isAnalog)
+ for (int j = 0; j < NUMBERS.size(); ++j)
+ {
+ if (NUMBERS[j]->name == name)
+ {
+ NUMBERS[j]->PreValue = stof(zwvalue.c_str());
+
+ time_t tStart;
+ int yy, month, dd, hh, mm, ss;
+ struct tm whenStart;
+
+ sscanf(zwtime.c_str(), PREVALUE_TIME_FORMAT_INPUT, &yy, &month, &dd, &hh, &mm, &ss);
+ whenStart.tm_year = yy - 1900;
+ whenStart.tm_mon = month - 1;
+ whenStart.tm_mday = dd;
+ whenStart.tm_hour = hh;
+ whenStart.tm_min = mm;
+ whenStart.tm_sec = ss;
+ whenStart.tm_isdst = -1;
+
+ NUMBERS[j]->lastvalue = mktime(&whenStart);
+
+ time(&tStart);
+ localtime(&tStart);
+ double difference = difftime(tStart, NUMBERS[j]->lastvalue);
+ difference /= 60;
+ if (difference > PreValueAgeStartup)
+ {
+ NUMBERS[j]->PreValueOkay = false;
+ }
+ else
+ {
+ NUMBERS[j]->PreValueOkay = true;
+ NUMBERS[j]->Value = NUMBERS[j]->PreValue;
+ NUMBERS[j]->ReturnValue = to_string(NUMBERS[j]->Value);
+ NUMBERS[j]->ReturnValueNoError = NUMBERS[j]->ReturnValue;
+
+ if (NUMBERS[j]->digit_roi || NUMBERS[j]->analog_roi)
+ {
+ NUMBERS[j]->ReturnValue = RundeOutput(NUMBERS[j]->Value, NUMBERS[j]->AnzahlAnalog - NUMBERS[j]->DecimalShift);
+ NUMBERS[j]->ReturnValueNoError = NUMBERS[j]->ReturnValue;
+ }
+ }
+
+ }
+ }
+
+ if (!fgets(zw, 1024, pFile))
+ _done = true;
+ else
+ {
+ printf("Read Zeile Prevalue.ini: %s", zw);
+ zerlegt = HelperZerlegeZeile(trim(std::string(zw)), "\t");
+ if (zerlegt.size() > 1)
+ {
+ name = trim(zerlegt[0]);
+ zwtime = trim(zerlegt[1]);
+ zwvalue = trim(zerlegt[2]);
+ }
+ }
+ }
+ fclose(pFile);
+ }
+ else // altes Format
{
- ReturnValue = RundeOutput(Value, AnzahlAnalog - DecimalShift);
- ReturnValueNoError = ReturnValue;
- }
-
+ fgets(zw, 1024, pFile);
+ fclose(pFile);
+ printf("%s", zw);
+ zwvalue = trim(std::string(zw));
+ NUMBERS[0]->PreValue = stof(zwvalue.c_str());
+
+ time_t tStart;
+ int yy, month, dd, hh, mm, ss;
+ struct tm whenStart;
+
+ sscanf(zwtime.c_str(), PREVALUE_TIME_FORMAT_INPUT, &yy, &month, &dd, &hh, &mm, &ss);
+ whenStart.tm_year = yy - 1900;
+ whenStart.tm_mon = month - 1;
+ whenStart.tm_mday = dd;
+ whenStart.tm_hour = hh;
+ whenStart.tm_min = mm;
+ whenStart.tm_sec = ss;
+ whenStart.tm_isdst = -1;
+
+ printf("TIME: %d, %d, %d, %d, %d, %d\n", whenStart.tm_year, whenStart.tm_mon, whenStart.tm_wday, whenStart.tm_hour, whenStart.tm_min, whenStart.tm_sec);
+
+ NUMBERS[0]->lastvalue = mktime(&whenStart);
+
+ time(&tStart);
+ localtime(&tStart);
+ double difference = difftime(tStart, NUMBERS[0]->lastvalue);
+ difference /= 60;
+ if (difference > PreValueAgeStartup)
+ return false;
+
+ NUMBERS[0]->Value = NUMBERS[0]->PreValue;
+ NUMBERS[0]->ReturnValue = to_string(NUMBERS[0]->Value);
+ NUMBERS[0]->ReturnValueNoError = NUMBERS[0]->ReturnValue;
+
+ if (NUMBERS[0]->digit_roi || NUMBERS[0]->analog_roi)
+ {
+ NUMBERS[0]->ReturnValue = RundeOutput(NUMBERS[0]->Value, NUMBERS[0]->AnzahlAnalog - NUMBERS[0]->DecimalShift);
+ NUMBERS[0]->ReturnValueNoError = NUMBERS[0]->ReturnValue;
+ }
+
+ UpdatePreValueINI = true; // Konvertierung ins neue Format
+ SavePreValue();
+ }
+
return true;
}
-void ClassFlowPostProcessing::SavePreValue(float value, string zwtime)
+void ClassFlowPostProcessing::SavePreValue()
{
FILE* pFile;
+ string _zw;
+
+ if (!UpdatePreValueINI) // PreValues unverändert --> File muss nicht neu geschrieben werden
+ return;
pFile = fopen(FilePreValue.c_str(), "w");
- if (strlen(zwtime.c_str()) == 0)
+ for (int j = 0; j < NUMBERS.size(); ++j)
{
- time_t rawtime;
- struct tm* timeinfo;
char buffer[80];
-
- time(&rawtime);
- timeinfo = localtime(&rawtime);
-
+ struct tm* timeinfo = localtime(&NUMBERS[j]->lastvalue);
strftime(buffer, 80, PREVALUE_TIME_FORMAT_OUTPUT, timeinfo);
- timeStamp = std::string(buffer);
- }
- else
- {
- timeStamp = zwtime;
+ NUMBERS[j]->timeStamp = std::string(buffer);
+
+ _zw = NUMBERS[j]->name + "\t" + NUMBERS[j]->timeStamp + "\t" + to_string(NUMBERS[j]->PreValue) + "\n";
+ printf("Write PreValue Zeile: %s\n", _zw.c_str());
+
+ fputs(_zw.c_str(), pFile);
}
- PreValue = value;
-
- fputs(timeStamp.c_str(), pFile);
- fputs("\n", pFile);
- fputs(to_string(value).c_str(), pFile);
- fputs("\n", pFile);
+ UpdatePreValueINI = false;
fclose(pFile);
}
@@ -149,22 +220,19 @@ void ClassFlowPostProcessing::SavePreValue(float value, string zwtime)
ClassFlowPostProcessing::ClassFlowPostProcessing(std::vector* lfc)
{
- FlowRateAct = 0;
+// FlowRateAct = 0;
PreValueUse = false;
PreValueAgeStartup = 30;
- AllowNegativeRates = false;
- MaxRateValue = 0.1;
ErrorMessage = false;
ListFlowControll = NULL;
- PreValueOkay = false;
- useMaxRateValue = false;
- checkDigitIncreaseConsistency = false;
- DecimalShift = 0;
- ErrorMessageText = "";
- timeStamp = "";
+// PreValueOkay = false;
+// DecimalShift = 0;
+// ErrorMessageText = "";
+// timeStamp = "";
FilePreValue = FormatFileName("/sdcard/config/prevalue.ini");
ListFlowControll = lfc;
flowMakeImage = NULL;
+ UpdatePreValueINI = false;
for (int i = 0; i < ListFlowControll->size(); ++i)
{
@@ -175,10 +243,35 @@ ClassFlowPostProcessing::ClassFlowPostProcessing(std::vector* lfc)
}
}
+void ClassFlowPostProcessing::handleDecimalSeparator(string _decsep, string _value)
+{
+ string _digit, _decpos;
+ int _pospunkt = _decsep.find_first_of(".");
+// printf("Name: %s, Pospunkt: %d\n", _name.c_str(), _pospunkt);
+ if (_pospunkt > -1)
+ {
+ _digit = _decsep.substr(_pospunkt+1, _decsep.length() - _pospunkt - 1);
+ }
+ else
+ {
+ _digit = "default";
+ }
+
+ for (int j = 0; j < NUMBERS.size(); ++j)
+ {
+ if (_digit == "default") // erstmal auf default setzen (falls sonst nichts gesetzt)
+ NUMBERS[j]->DecimalShift = stoi(_value);
+
+ if (NUMBERS[j]->name == _digit)
+ NUMBERS[j]->DecimalShift = stoi(_value);
+ }
+}
+
bool ClassFlowPostProcessing::ReadParameter(FILE* pfile, string& aktparamgraph)
{
std::vector zerlegt;
+ int _n;
aktparamgraph = trim(aktparamgraph);
@@ -190,12 +283,15 @@ bool ClassFlowPostProcessing::ReadParameter(FILE* pfile, string& aktparamgraph)
if (aktparamgraph.compare("[PostProcessing]") != 0) // Paragraph passt nich zu MakeImage
return false;
+ InitNUMBERS();
+
+
while (this->getNextLine(pfile, &aktparamgraph) && !this->isNewParagraph(aktparamgraph))
{
zerlegt = this->ZerlegeZeile(aktparamgraph);
- if ((toUpper(zerlegt[0]) == "DECIMALSHIFT") && (zerlegt.size() > 1))
+ if ((toUpper(zerlegt[0].substr(0, 12)) == "DECIMALSHIFT") && (zerlegt.size() > 1))
{
- DecimalShift = stoi(zerlegt[1]);
+ handleDecimalSeparator(zerlegt[0], zerlegt[1]);
}
if ((toUpper(zerlegt[0]) == "PREVALUEUSE") && (zerlegt.size() > 1))
@@ -208,12 +304,14 @@ bool ClassFlowPostProcessing::ReadParameter(FILE* pfile, string& aktparamgraph)
if ((toUpper(zerlegt[0]) == "CHECKDIGITINCREASECONSISTENCY") && (zerlegt.size() > 1))
{
if (toUpper(zerlegt[1]) == "TRUE")
- checkDigitIncreaseConsistency = true;
+ for (_n = 0; _n < NUMBERS.size(); ++_n)
+ NUMBERS[_n]->checkDigitIncreaseConsistency = true;
}
if ((toUpper(zerlegt[0]) == "ALLOWNEGATIVERATES") && (zerlegt.size() > 1))
{
if (toUpper(zerlegt[1]) == "TRUE")
- AllowNegativeRates = true;
+ for (_n = 0; _n < NUMBERS.size(); ++_n)
+ NUMBERS[_n]->AllowNegativeRates = true;
}
if ((toUpper(zerlegt[0]) == "ERRORMESSAGE") && (zerlegt.size() > 1))
{
@@ -226,17 +324,103 @@ bool ClassFlowPostProcessing::ReadParameter(FILE* pfile, string& aktparamgraph)
}
if ((toUpper(zerlegt[0]) == "MAXRATEVALUE") && (zerlegt.size() > 1))
{
- useMaxRateValue = true;
- MaxRateValue = std::stof(zerlegt[1]);
+ for (_n = 0; _n < NUMBERS.size(); ++_n)
+ {
+ NUMBERS[_n]->useMaxRateValue = true;
+ NUMBERS[_n]->MaxRateValue = std::stof(zerlegt[1]);
+ }
}
}
if (PreValueUse) {
- PreValueOkay = LoadPreValue();
+ LoadPreValue();
}
+
return true;
}
+void ClassFlowPostProcessing::InitNUMBERS()
+{
+// ClassFlowDigit* _cdigit = NULL;
+// ClassFlowAnalog* _canalog = NULL;
+ int anzDIGIT = 0;
+ int anzANALOG = 0;
+ std::vector name_numbers;
+
+ flowAnalog = NULL;
+ flowDigit = NULL;
+
+ for (int i = 0; i < ListFlowControll->size(); ++i)
+ {
+ if (((*ListFlowControll)[i])->name().compare("ClassFlowDigit") == 0)
+ {
+ flowDigit = (ClassFlowDigit*) (*ListFlowControll)[i];
+ anzDIGIT = flowDigit->getAnzahlDIGIT();
+ }
+ if (((*ListFlowControll)[i])->name().compare("ClassFlowAnalog") == 0)
+ {
+ flowAnalog = (ClassFlowAnalog*)(*ListFlowControll)[i];
+ anzANALOG = flowAnalog->getAnzahlANALOG();
+ }
+ }
+
+ flowDigit->UpdateNameNumbers(&name_numbers);
+ flowAnalog->UpdateNameNumbers(&name_numbers);
+
+ printf("Anzahl NUMBERS: %d - DIGITS: %d, ANALOG: %d\n", name_numbers.size(), anzDIGIT, anzANALOG);
+
+ for (int _num = 0; _num < name_numbers.size(); ++_num)
+ {
+ NumberPost *_number = new NumberPost;
+
+ _number->name = name_numbers[_num];
+
+ _number->digit_roi = NULL;
+ if (flowDigit)
+ _number->digit_roi = flowDigit->FindDIGIT(name_numbers[_num]);
+
+ if (_number->digit_roi)
+ _number->AnzahlDigital = _number->digit_roi->ROI.size();
+ else
+ _number->AnzahlDigital = 0;
+
+ _number->analog_roi = NULL;
+ if (flowAnalog)
+ _number->analog_roi = flowAnalog->FindANALOG(name_numbers[_num]);
+
+
+ if (_number->analog_roi)
+ _number->AnzahlAnalog = _number->analog_roi->ROI.size();
+ else
+ _number->AnzahlAnalog = 0;
+
+ _number->ReturnRawValue = ""; // Rohwert (mit N & führenden 0)
+ _number->ReturnValue = ""; // korrigierter Rückgabewert, ggf. mit Fehlermeldung
+ _number->ReturnValueNoError = ""; // korrigierter Rückgabewert ohne Fehlermeldung
+ _number->ErrorMessageText = ""; // Fehlermeldung bei Consistency Check
+ _number->PreValueOkay = false;
+ _number->AllowNegativeRates = false;
+ _number->MaxRateValue = 0.1;
+ _number->useMaxRateValue = false;
+ _number->checkDigitIncreaseConsistency = false;
+ _number->PreValueOkay = false;
+ _number->useMaxRateValue = false;
+
+ _number->FlowRateAct = 0; // m3 / min
+ _number->PreValue = 0; // letzter Wert, der gut ausgelesen wurde
+ _number->Value = 0; // letzer ausgelesener Wert, inkl. Korrekturen
+ _number->ReturnRawValue = ""; // Rohwert (mit N & führenden 0)
+ _number->ReturnValue = ""; // korrigierter Rückgabewert, ggf. mit Fehlermeldung
+ _number->ReturnValueNoError = ""; // korrigierter Rückgabewert ohne Fehlermeldung
+ _number->ErrorMessageText = ""; // Fehlermeldung bei Consistency Check
+
+ NUMBERS.push_back(_number);
+ }
+
+ for (int i = 0; i < NUMBERS.size(); ++i)
+ printf("Number %s, Anz DIG: %d, Anz ANA %d\n", NUMBERS[i]->name.c_str(), NUMBERS[i]->AnzahlDigital, NUMBERS[i]->AnzahlAnalog);
+}
+
string ClassFlowPostProcessing::ShiftDecimal(string in, int _decShift){
if (_decShift == 0){
@@ -285,173 +469,125 @@ bool ClassFlowPostProcessing::doFlow(string zwtime)
string digit = "";
string analog = "";
string zwvalue;
- bool isdigit = false;
- bool isanalog = false;
- int AnzahlAnalog = 0;
string zw;
time_t imagetime = 0;
string rohwert;
- ErrorMessageText = "";
-
-
- for (int i = 0; i < ListFlowControll->size(); ++i)
- {
- if (((*ListFlowControll)[i])->name().compare("ClassFlowMakeImage") == 0)
- {
- imagetime = ((ClassFlowMakeImage*)(*ListFlowControll)[i])->getTimeImageTaken();
- }
- if (((*ListFlowControll)[i])->name().compare("ClassFlowDigit") == 0)
- {
- isdigit = true;
- digit = (*ListFlowControll)[i]->getReadout();
- }
- if (((*ListFlowControll)[i])->name().compare("ClassFlowAnalog") == 0)
- {
- isanalog = true;
- analog = (*ListFlowControll)[i]->getReadout();
- AnzahlAnalog = ((ClassFlowAnalog*)(*ListFlowControll)[i])->AnzahlROIs();
- }
- }
+// ErrorMessageText = "";
+ imagetime = flowMakeImage->getTimeImageTaken();
if (imagetime == 0)
time(&imagetime);
struct tm* timeinfo;
timeinfo = localtime(&imagetime);
-
char strftime_buf[64];
strftime(strftime_buf, sizeof(strftime_buf), "%Y-%m-%dT%H:%M:%S", timeinfo);
zwtime = std::string(strftime_buf);
+ printf("Anzahl NUMBERS: %d\n", NUMBERS.size());
- // // TESTING ONLY////////////////////
- // isdigit = true; digit = "12N";
- // isanalog = true; analog = "456";
-
- ReturnRawValue = "";
-
- if (isdigit)
- ReturnRawValue = digit;
- if (isdigit && isanalog)
- ReturnRawValue = ReturnRawValue + ".";
- if (isanalog)
- ReturnRawValue = ReturnRawValue + analog;
-
-
- if (!isdigit)
+ for (int j = 0; j < NUMBERS.size(); ++j)
{
- AnzahlAnalog = 0;
- }
+ NUMBERS[j]->ReturnRawValue = "";
+ NUMBERS[j]->ErrorMessageText = "";
- ReturnRawValue = ShiftDecimal(ReturnRawValue, DecimalShift);
+ if (NUMBERS[j]->digit_roi)
+ NUMBERS[j]->ReturnRawValue = flowDigit->getReadout(j);
+ if (NUMBERS[j]->digit_roi && NUMBERS[j]->analog_roi)
+ NUMBERS[j]->ReturnRawValue = NUMBERS[j]->ReturnRawValue + ".";
+ if (NUMBERS[j]->analog_roi)
+ NUMBERS[j]->ReturnRawValue = NUMBERS[j]->ReturnRawValue + flowAnalog->getReadout(j);
- rohwert = ReturnRawValue;
+ NUMBERS[j]->ReturnRawValue = ShiftDecimal(NUMBERS[j]->ReturnRawValue, NUMBERS[j]->DecimalShift);
- if (!PreValueUse || !PreValueOkay)
- {
- ReturnValue = ReturnRawValue;
- ReturnValueNoError = ReturnRawValue;
+ rohwert = NUMBERS[j]->ReturnRawValue;
- if ((findDelimiterPos(ReturnValue, "N") == std::string::npos) && (ReturnValue.length() > 0))
+ if (!PreValueUse || !NUMBERS[j]->PreValueOkay)
{
- while ((ReturnValue.length() > 1) && (ReturnValue[0] == '0'))
- {
- ReturnValue.erase(0, 1);
- }
- Value = std::stof(ReturnValue);
- ReturnValueNoError = ReturnValue;
+ NUMBERS[j]->ReturnValue = NUMBERS[j]->ReturnRawValue;
+ NUMBERS[j]->ReturnValueNoError = NUMBERS[j]->ReturnRawValue;
- PreValueOkay = true;
- PreValue = Value;
- if (flowMakeImage)
+ if ((findDelimiterPos(NUMBERS[j]->ReturnValue, "N") == std::string::npos) && (NUMBERS[j]->ReturnValue.length() > 0))
{
- lastvalue = flowMakeImage->getTimeImageTaken();
- zwtime = ConvertTimeToString(lastvalue, PREVALUE_TIME_FORMAT_OUTPUT);
- }
- else
- {
- time(&lastvalue);
- localtime(&lastvalue);
- }
+ while ((NUMBERS[j]->ReturnValue.length() > 1) && (NUMBERS[j]->ReturnValue[0] == '0'))
+ {
+ NUMBERS[j]->ReturnValue.erase(0, 1);
+ }
+ NUMBERS[j]->Value = std::stof(NUMBERS[j]->ReturnValue);
+ NUMBERS[j]->ReturnValueNoError = NUMBERS[j]->ReturnValue;
- SavePreValue(Value, zwtime);
+ NUMBERS[j]->PreValueOkay = true;
+ NUMBERS[j]->PreValue = NUMBERS[j]->Value;
+ NUMBERS[j]->lastvalue = flowMakeImage->getTimeImageTaken();
+ zwtime = ConvertTimeToString(NUMBERS[j]->lastvalue, PREVALUE_TIME_FORMAT_OUTPUT);
+
+ UpdatePreValueINI = true;
+ SavePreValue();
+ }
+ }
+ else
+ {
+ zw = ErsetzteN(NUMBERS[j]->ReturnRawValue, NUMBERS[j]->PreValue);
+
+ NUMBERS[j]->Value = std::stof(zw);
+ if (NUMBERS[j]->checkDigitIncreaseConsistency)
+ {
+ NUMBERS[j]->Value = checkDigitConsistency(NUMBERS[j]->Value, NUMBERS[j]->DecimalShift, NUMBERS[j]->analog_roi != NULL, NUMBERS[j]->PreValue);
+ }
+
+ zwvalue = RundeOutput(NUMBERS[j]->Value, NUMBERS[j]->AnzahlAnalog - NUMBERS[j]->DecimalShift);
+
+ if ((!NUMBERS[j]->AllowNegativeRates) && (NUMBERS[j]->Value < NUMBERS[j]->PreValue))
+ {
+ NUMBERS[j]->ErrorMessageText = NUMBERS[j]->ErrorMessageText + "Neg. Rate - Read: " + zwvalue + " - Raw: " + NUMBERS[j]->ReturnRawValue + " - Pre: " + std::to_string(NUMBERS[j]->Value) + " ";
+ NUMBERS[j]->Value = NUMBERS[j]->PreValue;
+ zwvalue = RundeOutput(NUMBERS[j]->Value, NUMBERS[j]->AnzahlAnalog - NUMBERS[j]->DecimalShift);
+ }
+
+ if (NUMBERS[j]->useMaxRateValue && (abs(NUMBERS[j]->Value - NUMBERS[j]->PreValue) > NUMBERS[j]->MaxRateValue))
+ {
+ NUMBERS[j]->ErrorMessageText = NUMBERS[j]->ErrorMessageText + "Rate too high - Read: " + zwvalue + " - Pre: " + RundeOutput(NUMBERS[j]->Value, NUMBERS[j]->AnzahlAnalog - NUMBERS[j]->DecimalShift) + " ";
+ NUMBERS[j]->Value = NUMBERS[j]->PreValue;
+ zwvalue = RundeOutput(NUMBERS[j]->Value, NUMBERS[j]->AnzahlAnalog - NUMBERS[j]->DecimalShift);
+ }
+
+ NUMBERS[j]->ReturnValueNoError = zwvalue;
+ NUMBERS[j]->ReturnValue = zwvalue;
+ if (NUMBERS[j]->ErrorMessage && (NUMBERS[j]->ErrorMessageText.length() > 0))
+ NUMBERS[j]->ReturnValue = NUMBERS[j]->ReturnValue + "\t" + NUMBERS[j]->ErrorMessageText;
+
+
+ double difference = difftime(imagetime, NUMBERS[j]->lastvalue); // in Sekunden
+ difference /= 60; // in Minuten
+ NUMBERS[j]->FlowRateAct = (NUMBERS[j]->Value - NUMBERS[j]->PreValue) / difference;
+ NUMBERS[j]->lastvalue = imagetime;
+
+ if (NUMBERS[j]->ErrorMessageText.length() == 0)
+ {
+ NUMBERS[j]->PreValue = NUMBERS[j]->Value;
+ NUMBERS[j]->ErrorMessageText = "no error";
+ UpdatePreValueINI = true;
+ }
}
- return true;
}
-
- zw = ErsetzteN(ReturnRawValue);
-
- Value = std::stof(zw);
- if (checkDigitIncreaseConsistency)
- {
- Value = checkDigitConsistency(Value, DecimalShift, isanalog);
- }
-
- zwvalue = RundeOutput(Value, AnzahlAnalog - DecimalShift);
-
- if ((!AllowNegativeRates) && (Value < PreValue))
- {
- ErrorMessageText = ErrorMessageText + "Negative Rate - Returned old value - read value: " + zwvalue + " - raw value: " + ReturnRawValue + " - checked value: " + std::to_string(Value) + " ";
- Value = PreValue;
- zwvalue = RundeOutput(Value, AnzahlAnalog - DecimalShift);
- }
-
- if (useMaxRateValue && (abs(Value - PreValue) > MaxRateValue))
- {
- ErrorMessageText = ErrorMessageText + "Rate too high - Returned old value - read value: " + zwvalue + " - checked value: " + RundeOutput(Value, AnzahlAnalog - DecimalShift) + " ";
- Value = PreValue;
- zwvalue = RundeOutput(Value, AnzahlAnalog - DecimalShift);
- }
-
-
- ReturnValueNoError = zwvalue;
- ReturnValue = zwvalue;
- if (ErrorMessage && (ErrorMessageText.length() > 0))
- ReturnValue = ReturnValue + "\t" + ErrorMessageText;
-
- time_t currenttime;
- if (flowMakeImage)
- {
- currenttime = flowMakeImage->getTimeImageTaken();
- zwtime = ConvertTimeToString(currenttime, PREVALUE_TIME_FORMAT_OUTPUT);
- }
- else
- {
- time(¤ttime);
- localtime(¤ttime);
- }
-
- double difference = difftime(currenttime, lastvalue); // in Sekunden
- difference /= 60; // in Minuten
- FlowRateAct = (Value - PreValue) / difference;
- lastvalue = currenttime;
-// std::string _zw = "CalcRate: " + std::to_string(FlowRateAct) + " TimeDifference[min]: " + std::to_string(difference);
-// _zw = _zw + " Value: " + std::to_string(Value) + " PreValue: " + std::to_string(PreValue);
-// LogFile.WriteToFile(_zw);
-
- if (ErrorMessageText.length() == 0)
- {
- PreValue = Value;
- ErrorMessageText = "no error";
- SavePreValue(Value, zwtime);
- }
+ SavePreValue();
return true;
}
-string ClassFlowPostProcessing::getReadout()
+string ClassFlowPostProcessing::getReadout(int _number)
{
- return ReturnValue;
+ return NUMBERS[_number]->ReturnValue;
}
-string ClassFlowPostProcessing::getReadoutParam(bool _rawValue, bool _noerror)
+string ClassFlowPostProcessing::getReadoutParam(bool _rawValue, bool _noerror, int _number)
{
if (_rawValue)
- return ReturnRawValue;
+ return NUMBERS[_number]->ReturnRawValue;
if (_noerror)
- return ReturnValueNoError;
- return ReturnValue;
+ return NUMBERS[_number]->ReturnValueNoError;
+ return NUMBERS[_number]->ReturnValue;
}
string ClassFlowPostProcessing::RundeOutput(float _in, int _anzNachkomma){
@@ -478,7 +614,7 @@ string ClassFlowPostProcessing::RundeOutput(float _in, int _anzNachkomma){
}
-string ClassFlowPostProcessing::ErsetzteN(string input)
+string ClassFlowPostProcessing::ErsetzteN(string input, float _prevalue)
{
int posN, posPunkt;
int pot, ziffer;
@@ -499,7 +635,7 @@ string ClassFlowPostProcessing::ErsetzteN(string input)
pot = posPunkt - posN;
}
- zw = PreValue / pow(10, pot);
+ zw =_prevalue / pow(10, pot);
ziffer = ((int) zw) % 10;
input[posN] = ziffer + 48;
@@ -509,7 +645,7 @@ string ClassFlowPostProcessing::ErsetzteN(string input)
return input;
}
-float ClassFlowPostProcessing::checkDigitConsistency(float input, int _decilamshift, bool _isanalog){
+float ClassFlowPostProcessing::checkDigitConsistency(float input, int _decilamshift, bool _isanalog, float _preValue){
int aktdigit, olddigit;
int aktdigit_before, olddigit_before;
int pot, pot_max;
@@ -527,12 +663,12 @@ float ClassFlowPostProcessing::checkDigitConsistency(float input, int _decilamsh
{
zw = input / pow(10, pot-1);
aktdigit_before = ((int) zw) % 10;
- zw = PreValue / pow(10, pot-1);
+ zw = _preValue / pow(10, pot-1);
olddigit_before = ((int) zw) % 10;
zw = input / pow(10, pot);
aktdigit = ((int) zw) % 10;
- zw = PreValue / pow(10, pot);
+ zw = _preValue / pow(10, pot);
olddigit = ((int) zw) % 10;
no_nulldurchgang = (olddigit_before <= aktdigit_before);
@@ -558,18 +694,18 @@ float ClassFlowPostProcessing::checkDigitConsistency(float input, int _decilamsh
return input;
}
-string ClassFlowPostProcessing::getReadoutRate()
+string ClassFlowPostProcessing::getReadoutRate(int _number)
{
- return std::to_string(FlowRateAct);
+ return std::to_string(NUMBERS[_number]->FlowRateAct);
}
-string ClassFlowPostProcessing::getReadoutTimeStamp()
+string ClassFlowPostProcessing::getReadoutTimeStamp(int _number)
{
- return timeStamp;
+ return NUMBERS[_number]->timeStamp;
}
-string ClassFlowPostProcessing::getReadoutError()
+string ClassFlowPostProcessing::getReadoutError(int _number)
{
- return ErrorMessageText;
+ return NUMBERS[_number]->ErrorMessageText;
}
diff --git a/code/components/jomjol_flowcontroll/ClassFlowPostProcessing.h b/code/components/jomjol_flowcontroll/ClassFlowPostProcessing.h
index 8a115705..4e8858ec 100644
--- a/code/components/jomjol_flowcontroll/ClassFlowPostProcessing.h
+++ b/code/components/jomjol_flowcontroll/ClassFlowPostProcessing.h
@@ -1,58 +1,98 @@
#pragma once
#include "ClassFlow.h"
#include "ClassFlowMakeImage.h"
+#include "ClassFlowAnalog.h"
+#include "ClassFlowDigit.h"
+
#include
+struct NumberPost {
+// int PreValueAgeStartup;
+ float MaxRateValue;
+ bool useMaxRateValue;
+ bool ErrorMessage;
+ bool PreValueOkay;
+ bool AllowNegativeRates;
+ bool checkDigitIncreaseConsistency;
+ time_t lastvalue;
+ string timeStamp;
+ float FlowRateAct; // m3 / min
+ float PreValue; // letzter Wert, der gut ausgelesen wurde
+ float Value; // letzer ausgelesener Wert, inkl. Korrekturen
+ string ReturnRawValue; // Rohwert (mit N & führenden 0)
+ string ReturnValue; // korrigierter Rückgabewert, ggf. mit Fehlermeldung
+ string ReturnValueNoError; // korrigierter Rückgabewert ohne Fehlermeldung
+ string ErrorMessageText; // Fehlermeldung bei Consistency Check
+ int AnzahlAnalog;
+ int AnzahlDigital;
+ int DecimalShift;
+// ClassFlowAnalog* ANALOG;
+// ClassFlowDigit* DIGIT;
+
+ digit *digit_roi;
+ analog *analog_roi;
+
+
+
+ string name;
+};
+
+
class ClassFlowPostProcessing :
public ClassFlow
{
protected:
+ std::vector NUMBERS;
+ bool UpdatePreValueINI;
+
bool PreValueUse;
int PreValueAgeStartup;
- bool AllowNegativeRates;
- float MaxRateValue;
- bool useMaxRateValue;
+// bool AllowNegativeRates;
+// float MaxRateValue;
+// bool useMaxRateValue;
bool ErrorMessage;
- bool PreValueOkay;
- bool checkDigitIncreaseConsistency;
- int DecimalShift;
- time_t lastvalue;
- float FlowRateAct; // m3 / min
+ // bool PreValueOkay;
+// bool checkDigitIncreaseConsistency;
+// int DecimalShift;
+// time_t lastvalue;
+// float FlowRateAct; // m3 / min
+
+ ClassFlowAnalog* flowAnalog;
+ ClassFlowDigit* flowDigit;
string FilePreValue;
- float PreValue; // letzter Wert, der gut ausgelesen wurde
- float Value; // letzer ausgelesener Wert, inkl. Korrekturen
- string ReturnRawValue; // Rohwert (mit N & führenden 0)
- string ReturnValue; // korrigierter Rückgabewert, ggf. mit Fehlermeldung
- string ReturnValueNoError; // korrigierter Rückgabewert ohne Fehlermeldung
- string ErrorMessageText; // Fehlermeldung bei Consistency Check
- string timeStamp;
ClassFlowMakeImage *flowMakeImage;
bool LoadPreValue(void);
string ShiftDecimal(string in, int _decShift);
- string ErsetzteN(string);
- float checkDigitConsistency(float input, int _decilamshift, bool _isanalog);
+ string ErsetzteN(string, float _prevalue);
+ float checkDigitConsistency(float input, int _decilamshift, bool _isanalog, float _preValue);
string RundeOutput(float _in, int _anzNachkomma);
+ void InitNUMBERS();
+ void handleDecimalSeparator(string _decsep, string _value);
+
+
public:
ClassFlowPostProcessing(std::vector* lfc);
bool ReadParameter(FILE* pfile, string& aktparamgraph);
bool doFlow(string time);
- string getReadout();
- string getReadoutParam(bool _rawValue, bool _noerror);
- string getReadoutError();
- string getReadoutRate();
- string getReadoutTimeStamp();
- void SavePreValue(float value, string time = "");
- string GetPreValue();
+ string getReadout(int _number);
+ string getReadoutParam(bool _rawValue, bool _noerror, int _number = 0);
+ string getReadoutError(int _number = 0);
+ string getReadoutRate(int _number = 0);
+ string getReadoutTimeStamp(int _number = 0);
+ void SavePreValue();
+ string GetPreValue(std::string _number = "");
+ void SetPreValue(float zw, string _numbers);
+ std::vector GetNumbers(){return NUMBERS;};
string name(){return "ClassFlowPostProcessing";};
};
diff --git a/code/components/jomjol_helper/Helper.cpp b/code/components/jomjol_helper/Helper.cpp
index 0a32b30e..fdbd8644 100644
--- a/code/components/jomjol_helper/Helper.cpp
+++ b/code/components/jomjol_helper/Helper.cpp
@@ -10,6 +10,7 @@
#include
#include
+
#include "ClassLogFile.h"
//#include "ClassLogFile.h"
@@ -358,3 +359,30 @@ int removeFolder(const char* folderPath, const char* logTag) {
return deleted;
}
+
+
+
+std::vector HelperZerlegeZeile(std::string input, std::string _delimiter = "")
+{
+ std::vector Output;
+ std::string delimiter = " =,";
+ if (_delimiter.length() > 0){
+ delimiter = _delimiter;
+ }
+
+ input = trim(input, delimiter);
+ size_t pos = findDelimiterPos(input, delimiter);
+ std::string token;
+ while (pos != std::string::npos) {
+ token = input.substr(0, pos);
+ token = trim(token, delimiter);
+ Output.push_back(token);
+ input.erase(0, pos + 1);
+ input = trim(input, delimiter);
+ pos = findDelimiterPos(input, delimiter);
+ }
+ Output.push_back(input);
+
+ return Output;
+}
+
diff --git a/code/components/jomjol_helper/Helper.h b/code/components/jomjol_helper/Helper.h
index 5ecf2f5f..ac6cccae 100644
--- a/code/components/jomjol_helper/Helper.h
+++ b/code/components/jomjol_helper/Helper.h
@@ -1,6 +1,7 @@
#pragma once
#include
#include
+#include
using namespace std;
@@ -30,6 +31,8 @@ time_t addDays(time_t startTime, int days);
void memCopyGen(uint8_t* _source, uint8_t* _target, int _size);
+std::vector HelperZerlegeZeile(std::string input, std::string _delimiter);
+
///////////////////////////
size_t getInternalESPHeapSize();
size_t getESPHeapSize();
diff --git a/code/components/jomjol_tfliteclass/server_tflite.cpp b/code/components/jomjol_tfliteclass/server_tflite.cpp
index f29e384c..3859fd98 100644
--- a/code/components/jomjol_tfliteclass/server_tflite.cpp
+++ b/code/components/jomjol_tfliteclass/server_tflite.cpp
@@ -18,7 +18,7 @@
#include "ClassLogFile.h"
-//#define DEBUG_DETAIL_ON
+// #define DEBUG_DETAIL_ON
ClassFlowControll tfliteflow;
@@ -196,6 +196,8 @@ esp_err_t handler_wasserzaehler(httpd_req_t *req)
bool _rawValue = false;
bool _noerror = false;
+ bool _all = false;
+ std::string _type = "value";
string zw;
printf("handler_wasserzaehler uri:\n"); printf(req->uri); printf("\n");
@@ -206,6 +208,22 @@ esp_err_t handler_wasserzaehler(httpd_req_t *req)
if (httpd_req_get_url_query_str(req, _query, 100) == ESP_OK)
{
// printf("Query: "); printf(_query); printf("\n");
+ if (httpd_query_key_value(_query, "all", _size, 10) == ESP_OK)
+ {
+#ifdef DEBUG_DETAIL_ON
+ printf("all is found"); printf(_size); printf("\n");
+#endif
+ _all = true;
+ }
+
+ if (httpd_query_key_value(_query, "type", _size, 10) == ESP_OK)
+ {
+#ifdef DEBUG_DETAIL_ON
+ printf("all is found"); printf(_size); printf("\n");
+#endif
+ _type = std::string(_size);
+ }
+
if (httpd_query_key_value(_query, "rawvalue", _size, 10) == ESP_OK)
{
#ifdef DEBUG_DETAIL_ON
@@ -222,6 +240,29 @@ esp_err_t handler_wasserzaehler(httpd_req_t *req)
}
}
+ httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*");
+
+ if (_all)
+ {
+ httpd_resp_set_type(req, "text/plain");
+ printf("TYPE: %s\n", _type.c_str());
+ int _intype = READOUT_TYPE_VALUE;
+ if (_type == "prevalue")
+ _intype = READOUT_TYPE_PREVALUE;
+ if (_type == "raw")
+ _intype = READOUT_TYPE_RAWVALUE;
+ if (_type == "error")
+ _intype = READOUT_TYPE_ERROR;
+
+
+ zw = tfliteflow.getReadoutAll(_intype);
+ printf("ZW: %s\n", zw.c_str());
+ if (zw.length() > 0)
+ httpd_resp_sendstr_chunk(req, zw.c_str());
+ httpd_resp_sendstr_chunk(req, NULL);
+ return ESP_OK;
+ }
+
zw = tfliteflow.getReadout(_rawValue, _noerror);
if (zw.length() > 0)
httpd_resp_sendstr_chunk(req, zw.c_str());
@@ -498,6 +539,7 @@ esp_err_t handler_prevalue(httpd_req_t *req)
char _query[100];
char _size[10] = "";
+ char _numbers[50] = "default";
if (httpd_req_get_url_query_str(req, _query, 100) == ESP_OK)
{
@@ -511,15 +553,24 @@ esp_err_t handler_prevalue(httpd_req_t *req)
printf("Value: "); printf(_size); printf("\n");
#endif
}
+
+ httpd_query_key_value(_query, "numbers", _numbers, 50);
}
if (strlen(_size) == 0)
- zw = tfliteflow.GetPrevalue();
+ {
+ zw = tfliteflow.GetPrevalue(std::string(_numbers));
+ }
else
- zw = "SetPrevalue to " + tfliteflow.UpdatePrevalue(_size);
+ {
+ zw = "SetPrevalue to " + tfliteflow.UpdatePrevalue(_size, _numbers);
+ }
resp_str = zw.c_str();
+ httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*");
+
+
httpd_resp_send(req, resp_str, strlen(resp_str));
/* Respond with an empty chunk to signal HTTP response completion */
httpd_resp_send_chunk(req, NULL, 0);
diff --git a/code/components/jomjol_wlan/read_wlanini.cpp b/code/components/jomjol_wlan/read_wlanini.cpp
index dcf28a47..f7c7299c 100644
--- a/code/components/jomjol_wlan/read_wlanini.cpp
+++ b/code/components/jomjol_wlan/read_wlanini.cpp
@@ -70,7 +70,7 @@ void LoadWlanFromFile(std::string fn, char *&_ssid, char *&_password, char *&_ho
zerlegt = ZerlegeZeile(line, "=");
zerlegt[0] = trim(zerlegt[0], " ");
for (int i = 2; i < zerlegt.size(); ++i)
- zerlegt[1] = zerlegt[1] + zerlegt[i];
+ zerlegt[1] = zerlegt[1] + "=" + zerlegt[i];
if ((zerlegt.size() > 1) && (toUpper(zerlegt[0]) == "HOSTNAME")){
hostname = trim(zerlegt[1]);
diff --git a/code/main/server_main.cpp b/code/main/server_main.cpp
index ba31c09c..ac119e22 100644
--- a/code/main/server_main.cpp
+++ b/code/main/server_main.cpp
@@ -234,7 +234,7 @@ esp_err_t img_tmp_handler(httpd_req_t *req)
filetosend = filetosend + "/img_tmp/" + std::string(filename);
printf("File to upload: %s\n", filetosend.c_str());
- esp_err_t res = send_file(req, filetosend);
+ esp_err_t res = send_file(req, filetosend);
if (res != ESP_OK)
return res;
diff --git a/code/main/version.cpp b/code/main/version.cpp
index e4bb3dac..d726cb9a 100644
--- a/code/main/version.cpp
+++ b/code/main/version.cpp
@@ -1,4 +1,4 @@
-const char* GIT_REV="a000252";
+const char* GIT_REV="8308f15";
const char* GIT_TAG="";
-const char* GIT_BRANCH="master";
-const char* BUILD_TIME="2021-05-28 19:54";
\ No newline at end of file
+const char* GIT_BRANCH="rolling";
+const char* BUILD_TIME="2021-06-05 09:51";
\ No newline at end of file
diff --git a/code/platformio.ini b/code/platformio.ini
index 50bf0706..be60a9b8 100644
--- a/code/platformio.ini
+++ b/code/platformio.ini
@@ -37,5 +37,7 @@ lib_deps =
jomjol_controlGPIO
monitor_speed = 115200
+monitor_rts = 0
+monitor_dtr = 0
debug_tool = esp-prog
diff --git a/code/sdkconfig.old b/code/sdkconfig.old
index 757645d9..bb4711dd 100644
--- a/code/sdkconfig.old
+++ b/code/sdkconfig.old
@@ -131,8 +131,8 @@ CONFIG_EXAMPLE_CONNECT_IPV6=y
#
# Compiler options
#
-CONFIG_COMPILER_OPTIMIZATION_DEFAULT=y
-# CONFIG_COMPILER_OPTIMIZATION_SIZE is not set
+# CONFIG_COMPILER_OPTIMIZATION_DEFAULT is not set
+CONFIG_COMPILER_OPTIMIZATION_SIZE=y
# CONFIG_COMPILER_OPTIMIZATION_PERF is not set
# CONFIG_COMPILER_OPTIMIZATION_NONE is not set
CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_ENABLE=y
@@ -165,6 +165,8 @@ CONFIG_APPTRACE_LOCK_ENABLE=y
#
# CONFIG_BT_ENABLED is not set
CONFIG_BTDM_CTRL_BR_EDR_SCO_DATA_PATH_EFF=0
+CONFIG_BTDM_CTRL_PCM_ROLE_EFF=0
+CONFIG_BTDM_CTRL_PCM_POLAR_EFF=0
CONFIG_BTDM_CTRL_BLE_MAX_CONN_EFF=0
CONFIG_BTDM_CTRL_BR_EDR_MAX_ACL_CONN_EFF=0
CONFIG_BTDM_CTRL_BR_EDR_MAX_SYNC_CONN_EFF=0
@@ -204,6 +206,12 @@ CONFIG_SPI_MASTER_ISR_IN_IRAM=y
CONFIG_SPI_SLAVE_ISR_IN_IRAM=y
# end of SPI configuration
+#
+# CAN configuration
+#
+# CONFIG_CAN_ISR_IN_IRAM is not set
+# end of CAN configuration
+
#
# UART configuration
#
@@ -239,6 +247,7 @@ CONFIG_ESP_TLS_USING_MBEDTLS=y
#
# ESP32-specific
#
+CONFIG_ESP32_ECO3_CACHE_LOCK_FIX=y
CONFIG_ESP32_REV_MIN_0=y
# CONFIG_ESP32_REV_MIN_1 is not set
# CONFIG_ESP32_REV_MIN_2 is not set
@@ -598,7 +607,6 @@ CONFIG_FREERTOS_QUEUE_REGISTRY_SIZE=0
# CONFIG_FREERTOS_USE_TRACE_FACILITY is not set
# CONFIG_FREERTOS_GENERATE_RUN_TIME_STATS is not set
# CONFIG_FREERTOS_DEBUG_INTERNALS is not set
-CONFIG_FREERTOS_TASK_FUNCTION_WRAPPER=y
CONFIG_FREERTOS_CHECK_MUTEX_GIVEN_BY_OWNER=y
# CONFIG_FREERTOS_CHECK_PORT_CRITICAL_COMPLIANCE is not set
CONFIG_FREERTOS_DEBUG_OCDAWARE=y
@@ -657,8 +665,10 @@ CONFIG_LWIP_SO_REUSE=y
CONFIG_LWIP_SO_REUSE_RXTOALL=y
# CONFIG_LWIP_SO_RCVBUF is not set
# CONFIG_LWIP_NETBUF_RECVINFO is not set
-CONFIG_LWIP_IP_FRAG=y
-# CONFIG_LWIP_IP_REASSEMBLY is not set
+CONFIG_LWIP_IP4_FRAG=y
+CONFIG_LWIP_IP6_FRAG=y
+# CONFIG_LWIP_IP4_REASSEMBLY is not set
+# CONFIG_LWIP_IP6_REASSEMBLY is not set
# CONFIG_LWIP_STATS is not set
# CONFIG_LWIP_ETHARP_TRUST_IP_MAC is not set
CONFIG_LWIP_ESP_GRATUITOUS_ARP=y
@@ -682,8 +692,10 @@ CONFIG_LWIP_LOOPBACK_MAX_PBUFS=8
#
# TCP
#
+CONFIG_LWIP_TCP_ISN_HOOK=y
CONFIG_LWIP_MAX_ACTIVE_TCP=16
CONFIG_LWIP_MAX_LISTENING_TCP=16
+CONFIG_LWIP_TCP_HIGH_SPEED_RETRANSMISSION=y
CONFIG_LWIP_TCP_MAXRTX=12
CONFIG_LWIP_TCP_SYNMAXRTX=6
CONFIG_LWIP_TCP_MSS=1440
@@ -698,6 +710,7 @@ CONFIG_LWIP_TCP_QUEUE_OOSEQ=y
CONFIG_LWIP_TCP_OVERSIZE_MSS=y
# CONFIG_LWIP_TCP_OVERSIZE_QUARTER_MSS is not set
# CONFIG_LWIP_TCP_OVERSIZE_DISABLE is not set
+CONFIG_LWIP_TCP_RTO_TIME=1500
# end of TCP
#
@@ -713,6 +726,8 @@ CONFIG_LWIP_TCPIP_TASK_AFFINITY_NO_AFFINITY=y
# CONFIG_LWIP_TCPIP_TASK_AFFINITY_CPU1 is not set
CONFIG_LWIP_TCPIP_TASK_AFFINITY=0x7FFFFFFF
# CONFIG_LWIP_PPP_SUPPORT is not set
+CONFIG_LWIP_IPV6_MEMP_NUM_ND6_QUEUE=3
+CONFIG_LWIP_IPV6_ND6_NUM_NEIGHBORS=5
#
# ICMP
@@ -906,6 +921,7 @@ CONFIG_SPI_FLASH_DANGEROUS_WRITE_ABORTS=y
CONFIG_SPI_FLASH_YIELD_DURING_ERASE=y
CONFIG_SPI_FLASH_ERASE_YIELD_DURATION_MS=20
CONFIG_SPI_FLASH_ERASE_YIELD_TICKS=1
+# CONFIG_SPI_FLASH_SIZE_OVERRIDE is not set
#
# Auto-detect flash chips
@@ -913,6 +929,8 @@ CONFIG_SPI_FLASH_ERASE_YIELD_TICKS=1
CONFIG_SPI_FLASH_SUPPORT_ISSI_CHIP=y
CONFIG_SPI_FLASH_SUPPORT_GD_CHIP=y
# end of Auto-detect flash chips
+
+CONFIG_SPI_FLASH_ENABLE_ENCRYPTED_READ_WRITE=y
# end of SPI Flash driver
#
@@ -996,6 +1014,8 @@ CONFIG_WIFI_PROV_AUTOSTOP_TIMEOUT=30
#
CONFIG_WPA_MBEDTLS_CRYPTO=y
# CONFIG_WPA_TLS_V12 is not set
+# CONFIG_WPA_WPS_WARS is not set
+# CONFIG_WPA_DEBUG_PRINT is not set
# end of Supplicant
#
@@ -1020,3 +1040,152 @@ CONFIG_CAMERA_CORE0=y
#
# CONFIG_LEGACY_INCLUDE_COMMON_HEADERS is not set
# end of Compatibility options
+
+# Deprecated options for backward compatibility
+CONFIG_TOOLPREFIX="xtensa-esp32-elf-"
+# CONFIG_LOG_BOOTLOADER_LEVEL_NONE is not set
+# CONFIG_LOG_BOOTLOADER_LEVEL_ERROR is not set
+# CONFIG_LOG_BOOTLOADER_LEVEL_WARN is not set
+CONFIG_LOG_BOOTLOADER_LEVEL_INFO=y
+# CONFIG_LOG_BOOTLOADER_LEVEL_DEBUG is not set
+# CONFIG_LOG_BOOTLOADER_LEVEL_VERBOSE is not set
+CONFIG_LOG_BOOTLOADER_LEVEL=3
+# CONFIG_APP_ROLLBACK_ENABLE is not set
+# CONFIG_FLASH_ENCRYPTION_ENABLED is not set
+# CONFIG_FLASHMODE_QIO is not set
+# CONFIG_FLASHMODE_QOUT is not set
+CONFIG_FLASHMODE_DIO=y
+# CONFIG_FLASHMODE_DOUT is not set
+# CONFIG_MONITOR_BAUD_9600B is not set
+# CONFIG_MONITOR_BAUD_57600B is not set
+CONFIG_MONITOR_BAUD_115200B=y
+# CONFIG_MONITOR_BAUD_230400B is not set
+# CONFIG_MONITOR_BAUD_921600B is not set
+# CONFIG_MONITOR_BAUD_2MB is not set
+# CONFIG_MONITOR_BAUD_OTHER is not set
+CONFIG_MONITOR_BAUD_OTHER_VAL=115200
+CONFIG_MONITOR_BAUD=115200
+# CONFIG_COMPILER_OPTIMIZATION_LEVEL_DEBUG is not set
+CONFIG_COMPILER_OPTIMIZATION_LEVEL_RELEASE=y
+CONFIG_OPTIMIZATION_ASSERTIONS_ENABLED=y
+# CONFIG_OPTIMIZATION_ASSERTIONS_SILENT is not set
+# CONFIG_OPTIMIZATION_ASSERTIONS_DISABLED is not set
+# CONFIG_CXX_EXCEPTIONS is not set
+CONFIG_STACK_CHECK_NONE=y
+# CONFIG_STACK_CHECK_NORM is not set
+# CONFIG_STACK_CHECK_STRONG is not set
+# CONFIG_STACK_CHECK_ALL is not set
+# CONFIG_WARN_WRITE_STRINGS is not set
+# CONFIG_DISABLE_GCC8_WARNINGS is not set
+# CONFIG_ESP32_APPTRACE_DEST_TRAX is not set
+CONFIG_ESP32_APPTRACE_DEST_NONE=y
+CONFIG_ESP32_APPTRACE_LOCK_ENABLE=y
+CONFIG_BTDM_CONTROLLER_BLE_MAX_CONN_EFF=0
+CONFIG_BTDM_CONTROLLER_BR_EDR_MAX_ACL_CONN_EFF=0
+CONFIG_BTDM_CONTROLLER_BR_EDR_MAX_SYNC_CONN_EFF=0
+CONFIG_BTDM_CONTROLLER_PINNED_TO_CORE=0
+CONFIG_ADC2_DISABLE_DAC=y
+CONFIG_SPIRAM_SUPPORT=y
+# CONFIG_WIFI_LWIP_ALLOCATION_FROM_SPIRAM_FIRST is not set
+CONFIG_TRACEMEM_RESERVE_DRAM=0x0
+# CONFIG_TWO_UNIVERSAL_MAC_ADDRESS is not set
+CONFIG_FOUR_UNIVERSAL_MAC_ADDRESS=y
+CONFIG_NUMBER_OF_UNIVERSAL_MAC_ADDRESS=4
+# CONFIG_ULP_COPROC_ENABLED is not set
+CONFIG_ULP_COPROC_RESERVE_MEM=0
+CONFIG_BROWNOUT_DET=y
+CONFIG_BROWNOUT_DET_LVL_SEL_0=y
+# CONFIG_BROWNOUT_DET_LVL_SEL_1 is not set
+# CONFIG_BROWNOUT_DET_LVL_SEL_2 is not set
+# CONFIG_BROWNOUT_DET_LVL_SEL_3 is not set
+# CONFIG_BROWNOUT_DET_LVL_SEL_4 is not set
+# CONFIG_BROWNOUT_DET_LVL_SEL_5 is not set
+# CONFIG_BROWNOUT_DET_LVL_SEL_6 is not set
+# CONFIG_BROWNOUT_DET_LVL_SEL_7 is not set
+CONFIG_BROWNOUT_DET_LVL=0
+CONFIG_REDUCE_PHY_TX_POWER=y
+CONFIG_ESP32_RTC_CLOCK_SOURCE_INTERNAL_RC=y
+# CONFIG_ESP32_RTC_CLOCK_SOURCE_EXTERNAL_CRYSTAL is not set
+# CONFIG_ESP32_RTC_CLOCK_SOURCE_EXTERNAL_OSC is not set
+# CONFIG_ESP32_RTC_CLOCK_SOURCE_INTERNAL_8MD256 is not set
+# CONFIG_DISABLE_BASIC_ROM_CONSOLE is not set
+# CONFIG_NO_BLOBS is not set
+# CONFIG_COMPATIBLE_PRE_V2_1_BOOTLOADERS is not set
+CONFIG_SYSTEM_EVENT_QUEUE_SIZE=32
+CONFIG_SYSTEM_EVENT_TASK_STACK_SIZE=2304
+CONFIG_MAIN_TASK_STACK_SIZE=3584
+CONFIG_IPC_TASK_STACK_SIZE=1024
+CONFIG_TIMER_TASK_STACK_SIZE=3584
+CONFIG_CONSOLE_UART_DEFAULT=y
+# CONFIG_CONSOLE_UART_CUSTOM is not set
+# CONFIG_CONSOLE_UART_NONE is not set
+CONFIG_CONSOLE_UART_NUM=0
+CONFIG_CONSOLE_UART_BAUDRATE=115200
+CONFIG_INT_WDT=y
+CONFIG_INT_WDT_TIMEOUT_MS=300
+CONFIG_INT_WDT_CHECK_CPU1=y
+CONFIG_TASK_WDT=y
+# CONFIG_TASK_WDT_PANIC is not set
+CONFIG_TASK_WDT_TIMEOUT_S=3
+CONFIG_TASK_WDT_CHECK_IDLE_TASK_CPU0=y
+CONFIG_TASK_WDT_CHECK_IDLE_TASK_CPU1=y
+# CONFIG_EVENT_LOOP_PROFILING is not set
+CONFIG_POST_EVENTS_FROM_ISR=y
+CONFIG_POST_EVENTS_FROM_IRAM_ISR=y
+CONFIG_MB_MASTER_TIMEOUT_MS_RESPOND=150
+CONFIG_MB_MASTER_DELAY_MS_CONVERT=200
+CONFIG_MB_QUEUE_LENGTH=20
+CONFIG_MB_SERIAL_TASK_STACK_SIZE=2048
+CONFIG_MB_SERIAL_BUF_SIZE=256
+CONFIG_MB_SERIAL_TASK_PRIO=10
+# CONFIG_MB_CONTROLLER_SLAVE_ID_SUPPORT is not set
+CONFIG_MB_CONTROLLER_NOTIFY_TIMEOUT=20
+CONFIG_MB_CONTROLLER_NOTIFY_QUEUE_SIZE=20
+CONFIG_MB_CONTROLLER_STACK_SIZE=4096
+CONFIG_MB_EVENT_QUEUE_TIMEOUT=20
+CONFIG_MB_TIMER_PORT_ENABLED=y
+CONFIG_MB_TIMER_GROUP=0
+CONFIG_MB_TIMER_INDEX=0
+CONFIG_SUPPORT_STATIC_ALLOCATION=y
+# CONFIG_ENABLE_STATIC_TASK_CLEAN_UP_HOOK is not set
+CONFIG_TIMER_TASK_PRIORITY=1
+CONFIG_TIMER_TASK_STACK_DEPTH=2048
+CONFIG_TIMER_QUEUE_LENGTH=10
+# CONFIG_L2_TO_L3_COPY is not set
+# CONFIG_USE_ONLY_LWIP_SELECT is not set
+CONFIG_ESP_GRATUITOUS_ARP=y
+CONFIG_GARP_TMR_INTERVAL=60
+CONFIG_TCPIP_RECVMBOX_SIZE=32
+CONFIG_TCP_MAXRTX=12
+CONFIG_TCP_SYNMAXRTX=6
+CONFIG_TCP_MSS=1440
+CONFIG_TCP_MSL=60000
+CONFIG_TCP_SND_BUF_DEFAULT=5744
+CONFIG_TCP_WND_DEFAULT=5744
+CONFIG_TCP_RECVMBOX_SIZE=6
+CONFIG_TCP_QUEUE_OOSEQ=y
+# CONFIG_ESP_TCP_KEEP_CONNECTION_WHEN_IP_CHANGES is not set
+CONFIG_TCP_OVERSIZE_MSS=y
+# CONFIG_TCP_OVERSIZE_QUARTER_MSS is not set
+# CONFIG_TCP_OVERSIZE_DISABLE is not set
+CONFIG_UDP_RECVMBOX_SIZE=6
+CONFIG_TCPIP_TASK_STACK_SIZE=3072
+CONFIG_TCPIP_TASK_AFFINITY_NO_AFFINITY=y
+# CONFIG_TCPIP_TASK_AFFINITY_CPU0 is not set
+# CONFIG_TCPIP_TASK_AFFINITY_CPU1 is not set
+CONFIG_TCPIP_TASK_AFFINITY=0x7FFFFFFF
+# CONFIG_PPP_SUPPORT is not set
+CONFIG_ESP32_PTHREAD_TASK_PRIO_DEFAULT=5
+CONFIG_ESP32_PTHREAD_TASK_STACK_SIZE_DEFAULT=3072
+CONFIG_ESP32_PTHREAD_STACK_MIN=768
+CONFIG_ESP32_DEFAULT_PTHREAD_CORE_NO_AFFINITY=y
+# CONFIG_ESP32_DEFAULT_PTHREAD_CORE_0 is not set
+# CONFIG_ESP32_DEFAULT_PTHREAD_CORE_1 is not set
+CONFIG_ESP32_PTHREAD_TASK_CORE_DEFAULT=-1
+CONFIG_ESP32_PTHREAD_TASK_NAME_DEFAULT="pthread"
+CONFIG_SPI_FLASH_WRITING_DANGEROUS_REGIONS_ABORTS=y
+# CONFIG_SPI_FLASH_WRITING_DANGEROUS_REGIONS_FAILS is not set
+# CONFIG_SPI_FLASH_WRITING_DANGEROUS_REGIONS_ALLOWED is not set
+# CONFIG_SUPPRESS_SELECT_DEBUG_OUTPUT is not set
+CONFIG_SUPPORT_TERMIOS=y
+# End of deprecated options
diff --git a/code/version.cpp b/code/version.cpp
index 499917b1..d726cb9a 100644
--- a/code/version.cpp
+++ b/code/version.cpp
@@ -1,4 +1,4 @@
-const char* GIT_REV="a000252";
+const char* GIT_REV="8308f15";
const char* GIT_TAG="";
-const char* GIT_BRANCH="master";
-const char* BUILD_TIME="2021-05-28 19:53";
\ No newline at end of file
+const char* GIT_BRANCH="rolling";
+const char* BUILD_TIME="2021-06-05 09:51";
\ No newline at end of file
diff --git a/firmware/bootloader.bin b/firmware/bootloader.bin
index 1ec39263d5917eaaf1ab1a928a6ecca2b584e2f6..a20e797810c8686bfacff6e466a01e29467345bc 100644
GIT binary patch
delta 61
zcmdmRlyL(P$q929SX!AHTNxTnW@M4rY$qJ(ulQ2HMnCy>i+J~MO__q5Tv74IN+w)h
Q8L`a9L|Mpk!Ycl60D_PfXaE2J
delta 61
zcmdmRlyL(P$q929T3VT!S{YePW@M4rY$qJ(ulW4@Or5@YfmaSn9CVvebm_Y1v4H1y
R9;>@<+48Y!>w`bJ69L-194`O>
diff --git a/firmware/firmware.bin b/firmware/firmware.bin
index b1346232ba1cf91f1c7573899a8077ca5e35e1b5..242ce28cdf74be1341bbfacc55902c750ae82ab5 100644
GIT binary patch
delta 451489
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