mirror of
https://github.com/jomjol/AI-on-the-edge-device.git
synced 2026-01-01 14:19:03 +03:00
Add error handling for memory intensive tasks (#1798)
* tflite model loading: error handling * FlowAlignment: error handling * CImageBasis+GetJPGStream : error handling
This commit is contained in:
@@ -12,8 +12,6 @@
|
||||
|
||||
static const char *TAG = "ALIGN";
|
||||
|
||||
bool AlignmentExtendedDebugging = true;
|
||||
|
||||
// #define DEBUG_DETAIL_ON
|
||||
|
||||
|
||||
@@ -36,6 +34,7 @@ void ClassFlowAlignment::SetInitialParameter(void)
|
||||
SAD_criteria = 0.05;
|
||||
}
|
||||
|
||||
|
||||
ClassFlowAlignment::ClassFlowAlignment(std::vector<ClassFlow*>* lfc)
|
||||
{
|
||||
SetInitialParameter();
|
||||
@@ -51,7 +50,7 @@ ClassFlowAlignment::ClassFlowAlignment(std::vector<ClassFlow*>* lfc)
|
||||
|
||||
if (!ImageBasis) // the function take pictures does not exist --> must be created first ONLY FOR TEST PURPOSES
|
||||
{
|
||||
if (AlignmentExtendedDebugging) ESP_LOGD(TAG, "CImageBasis had to be created");
|
||||
ESP_LOGD(TAG, "CImageBasis had to be created");
|
||||
ImageBasis = new CImageBasis(namerawimage);
|
||||
}
|
||||
}
|
||||
@@ -119,10 +118,10 @@ bool ClassFlowAlignment::ReadParameter(FILE* pfile, string& aktparamgraph)
|
||||
}
|
||||
if ((toUpper(splitted[0]) == "ALIGNMENTALGO") && (splitted.size() > 1))
|
||||
{
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
std::string zw2 = "Alignment mode selected: " + splitted[1];
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, zw2);
|
||||
#endif
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
std::string zw2 = "Alignment mode selected: " + splitted[1];
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, zw2);
|
||||
#endif
|
||||
if (toUpper(splitted[1]) == "HIGHACCURACY")
|
||||
alg_algo = 1;
|
||||
if (toUpper(splitted[1]) == "FAST")
|
||||
@@ -136,10 +135,10 @@ bool ClassFlowAlignment::ReadParameter(FILE* pfile, string& aktparamgraph)
|
||||
References[i].search_y = suchey;
|
||||
References[i].fastalg_SAD_criteria = SAD_criteria;
|
||||
References[i].alignment_algo = alg_algo;
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
std::string zw2 = "Alignment mode written: " + std::to_string(alg_algo);
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, zw2);
|
||||
#endif
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
std::string zw2 = "Alignment mode written: " + std::to_string(alg_algo);
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, zw2);
|
||||
#endif
|
||||
}
|
||||
|
||||
LoadReferenceAlignmentValues();
|
||||
@@ -148,6 +147,7 @@ bool ClassFlowAlignment::ReadParameter(FILE* pfile, string& aktparamgraph)
|
||||
|
||||
}
|
||||
|
||||
|
||||
string ClassFlowAlignment::getHTMLSingleStep(string host)
|
||||
{
|
||||
string result;
|
||||
@@ -162,11 +162,24 @@ string ClassFlowAlignment::getHTMLSingleStep(string host)
|
||||
bool ClassFlowAlignment::doFlow(string time)
|
||||
{
|
||||
if (!ImageTMP)
|
||||
{
|
||||
ImageTMP = new CImageBasis(ImageBasis);
|
||||
if (!ImageTMP)
|
||||
{
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Can't allocate ImageTMP -> Exec this round aborted!");
|
||||
LogFile.WriteHeapInfo("ClassFlowAlignment-doFlow");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
delete AlignAndCutImage;
|
||||
|
||||
AlignAndCutImage = new CAlignAndCutImage(ImageBasis, ImageTMP);
|
||||
AlignAndCutImage = new CAlignAndCutImage(ImageBasis, ImageTMP);
|
||||
if (!AlignAndCutImage)
|
||||
{
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Can't allocate AlignAndCutImage -> Exec this round aborted!");
|
||||
LogFile.WriteHeapInfo("ClassFlowAlignment-doFlow");
|
||||
return false;
|
||||
}
|
||||
|
||||
CRotateImage rt(AlignAndCutImage, ImageTMP, initialflip);
|
||||
if (initialflip)
|
||||
@@ -176,10 +189,13 @@ bool ClassFlowAlignment::doFlow(string time)
|
||||
ImageBasis->width = _zw;
|
||||
}
|
||||
|
||||
if (initialmirror){
|
||||
if (initialmirror)
|
||||
{
|
||||
ESP_LOGD(TAG, "do mirror");
|
||||
rt.Mirror();
|
||||
if (SaveAllFiles) AlignAndCutImage->SaveToFile(FormatFileName("/sdcard/img_tmp/mirror.jpg"));
|
||||
|
||||
if (SaveAllFiles)
|
||||
AlignAndCutImage->SaveToFile(FormatFileName("/sdcard/img_tmp/mirror.jpg"));
|
||||
}
|
||||
|
||||
if ((initalrotate != 0) || initialflip)
|
||||
@@ -188,7 +204,9 @@ bool ClassFlowAlignment::doFlow(string time)
|
||||
rt.RotateAntiAliasing(initalrotate);
|
||||
else
|
||||
rt.Rotate(initalrotate);
|
||||
if (SaveAllFiles) AlignAndCutImage->SaveToFile(FormatFileName("/sdcard/img_tmp/rot.jpg"));
|
||||
|
||||
if (SaveAllFiles)
|
||||
AlignAndCutImage->SaveToFile(FormatFileName("/sdcard/img_tmp/rot.jpg"));
|
||||
}
|
||||
|
||||
if (!AlignAndCutImage->Align(&References[0], &References[1]))
|
||||
@@ -213,14 +231,13 @@ bool ClassFlowAlignment::doFlow(string time)
|
||||
// must be deleted to have memory space for loading tflite
|
||||
delete ImageTMP;
|
||||
ImageTMP = NULL;
|
||||
|
||||
|
||||
LoadReferenceAlignmentValues();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void ClassFlowAlignment::SaveReferenceAlignmentValues()
|
||||
{
|
||||
FILE* pFile;
|
||||
@@ -260,9 +277,6 @@ void ClassFlowAlignment::SaveReferenceAlignmentValues()
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
bool ClassFlowAlignment::LoadReferenceAlignmentValues(void)
|
||||
{
|
||||
FILE* pFile;
|
||||
@@ -311,25 +325,26 @@ bool ClassFlowAlignment::LoadReferenceAlignmentValues(void)
|
||||
fclose(pFile);
|
||||
|
||||
|
||||
/*#ifdef DEBUG_DETAIL_ON
|
||||
std::string _zw = "\tLoadReferences[0]\tx,y:\t" + std::to_string(References[0].fastalg_x) + "\t" + std::to_string(References[0].fastalg_x);
|
||||
_zw = _zw + "\tSAD, min, max, avg:\t" + std::to_string(References[0].fastalg_SAD) + "\t" + std::to_string(References[0].fastalg_min);
|
||||
_zw = _zw + "\t" + std::to_string(References[0].fastalg_max) + "\t" + std::to_string(References[0].fastalg_avg);
|
||||
LogFile.WriteToDedicatedFile("/sdcard/alignment.txt", _zw);
|
||||
_zw = "\tLoadReferences[1]\tx,y:\t" + std::to_string(References[1].fastalg_x) + "\t" + std::to_string(References[1].fastalg_x);
|
||||
_zw = _zw + "\tSAD, min, max, avg:\t" + std::to_string(References[1].fastalg_SAD) + "\t" + std::to_string(References[1].fastalg_min);
|
||||
_zw = _zw + "\t" + std::to_string(References[1].fastalg_max) + "\t" + std::to_string(References[1].fastalg_avg);
|
||||
LogFile.WriteToDedicatedFile("/sdcard/alignment.txt", _zw);
|
||||
#endif*/
|
||||
/*#ifdef DEBUG_DETAIL_ON
|
||||
std::string _zw = "\tLoadReferences[0]\tx,y:\t" + std::to_string(References[0].fastalg_x) + "\t" + std::to_string(References[0].fastalg_x);
|
||||
_zw = _zw + "\tSAD, min, max, avg:\t" + std::to_string(References[0].fastalg_SAD) + "\t" + std::to_string(References[0].fastalg_min);
|
||||
_zw = _zw + "\t" + std::to_string(References[0].fastalg_max) + "\t" + std::to_string(References[0].fastalg_avg);
|
||||
LogFile.WriteToDedicatedFile("/sdcard/alignment.txt", _zw);
|
||||
_zw = "\tLoadReferences[1]\tx,y:\t" + std::to_string(References[1].fastalg_x) + "\t" + std::to_string(References[1].fastalg_x);
|
||||
_zw = _zw + "\tSAD, min, max, avg:\t" + std::to_string(References[1].fastalg_SAD) + "\t" + std::to_string(References[1].fastalg_min);
|
||||
_zw = _zw + "\t" + std::to_string(References[1].fastalg_max) + "\t" + std::to_string(References[1].fastalg_avg);
|
||||
LogFile.WriteToDedicatedFile("/sdcard/alignment.txt", _zw);
|
||||
#endif*/
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void ClassFlowAlignment::DrawRef(CImageBasis *_zw)
|
||||
{
|
||||
_zw->drawRect(References[0].target_x, References[0].target_y, References[0].width, References[0].height, 255, 0, 0, 2);
|
||||
_zw->drawRect(References[1].target_x, References[1].target_y, References[1].width, References[1].height, 255, 0, 0, 2);
|
||||
if (_zw->ImageOkay())
|
||||
{
|
||||
_zw->drawRect(References[0].target_x, References[0].target_y, References[0].width, References[0].height, 255, 0, 0, 2);
|
||||
_zw->drawRect(References[1].target_x, References[1].target_y, References[1].width, References[1].height, 255, 0, 0, 2);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
|
||||
static const char* TAG = "CNN";
|
||||
|
||||
//#define DEBUG_DETAIL_ON
|
||||
|
||||
|
||||
ClassFlowCNNGeneral::ClassFlowCNNGeneral(ClassFlowAlignment *_flowalign, t_CNNType _cnntype) : ClassFlowImage(NULL, TAG)
|
||||
@@ -31,6 +32,7 @@ ClassFlowCNNGeneral::ClassFlowCNNGeneral(ClassFlowAlignment *_flowalign, t_CNNTy
|
||||
logfileRetentionInDays = 5;
|
||||
}
|
||||
|
||||
|
||||
string ClassFlowCNNGeneral::getReadout(int _analog = 0, bool _extendedResolution, int prev, float _before_narrow_Analog, float analogDigitalTransitionStart)
|
||||
{
|
||||
string result = "";
|
||||
@@ -124,11 +126,10 @@ string ClassFlowCNNGeneral::getReadout(int _analog = 0, bool _extendedResolution
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
int ClassFlowCNNGeneral::PointerEvalHybridNew(float number, float number_of_predecessors, int eval_predecessors, bool Analog_Predecessors, float digitalAnalogTransitionStart)
|
||||
{
|
||||
int result;
|
||||
@@ -245,6 +246,7 @@ int ClassFlowCNNGeneral::PointerEvalAnalogToDigitNew(float number, float numeral
|
||||
|
||||
}
|
||||
|
||||
|
||||
int ClassFlowCNNGeneral::PointerEvalAnalogNew(float number, int numeral_preceder)
|
||||
{
|
||||
float number_min, number_max;
|
||||
@@ -285,7 +287,6 @@ int ClassFlowCNNGeneral::PointerEvalAnalogNew(float number, int numeral_preceder
|
||||
" number: " + std::to_string(number) + " numeral_preceder = " + std::to_string(numeral_preceder) + " Analog_error = " + std::to_string(Analog_error));
|
||||
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -382,6 +383,7 @@ bool ClassFlowCNNGeneral::ReadParameter(FILE* pfile, string& aktparamgraph)
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
general* ClassFlowCNNGeneral::FindGENERAL(string _name_number)
|
||||
{
|
||||
for (int i = 0; i < GENERAL.size(); ++i)
|
||||
@@ -434,7 +436,6 @@ general* ClassFlowCNNGeneral::GetGENERAL(string _name, bool _create = true)
|
||||
}
|
||||
|
||||
|
||||
|
||||
string ClassFlowCNNGeneral::getHTMLSingleStep(string host)
|
||||
{
|
||||
string result, zw;
|
||||
@@ -459,7 +460,6 @@ string ClassFlowCNNGeneral::getHTMLSingleStep(string host)
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool ClassFlowCNNGeneral::doFlow(string time)
|
||||
{
|
||||
if (disabled)
|
||||
@@ -477,6 +477,7 @@ bool ClassFlowCNNGeneral::doFlow(string time)
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool ClassFlowCNNGeneral::doAlignAndCut(string time)
|
||||
{
|
||||
if (disabled)
|
||||
@@ -511,31 +512,36 @@ bool ClassFlowCNNGeneral::doAlignAndCut(string time)
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void ClassFlowCNNGeneral::DrawROI(CImageBasis *_zw)
|
||||
{
|
||||
if (CNNType == Analogue || CNNType == Analogue100)
|
||||
{
|
||||
int r = 0;
|
||||
int g = 255;
|
||||
int b = 0;
|
||||
if (_zw->ImageOkay())
|
||||
{
|
||||
if (CNNType == Analogue || CNNType == Analogue100)
|
||||
{
|
||||
int r = 0;
|
||||
int g = 255;
|
||||
int b = 0;
|
||||
|
||||
for (int _ana = 0; _ana < GENERAL.size(); ++_ana)
|
||||
for (int i = 0; i < GENERAL[_ana]->ROI.size(); ++i)
|
||||
{
|
||||
_zw->drawRect(GENERAL[_ana]->ROI[i]->posx, GENERAL[_ana]->ROI[i]->posy, GENERAL[_ana]->ROI[i]->deltax, GENERAL[_ana]->ROI[i]->deltay, r, g, b, 1);
|
||||
_zw->drawEllipse( (int) (GENERAL[_ana]->ROI[i]->posx + GENERAL[_ana]->ROI[i]->deltax/2), (int) (GENERAL[_ana]->ROI[i]->posy + GENERAL[_ana]->ROI[i]->deltay/2), (int) (GENERAL[_ana]->ROI[i]->deltax/2), (int) (GENERAL[_ana]->ROI[i]->deltay/2), r, g, b, 2);
|
||||
_zw->drawLine((int) (GENERAL[_ana]->ROI[i]->posx + GENERAL[_ana]->ROI[i]->deltax/2), (int) GENERAL[_ana]->ROI[i]->posy, (int) (GENERAL[_ana]->ROI[i]->posx + GENERAL[_ana]->ROI[i]->deltax/2), (int) (GENERAL[_ana]->ROI[i]->posy + GENERAL[_ana]->ROI[i]->deltay), r, g, b, 2);
|
||||
_zw->drawLine((int) GENERAL[_ana]->ROI[i]->posx, (int) (GENERAL[_ana]->ROI[i]->posy + GENERAL[_ana]->ROI[i]->deltay/2), (int) GENERAL[_ana]->ROI[i]->posx + GENERAL[_ana]->ROI[i]->deltax, (int) (GENERAL[_ana]->ROI[i]->posy + GENERAL[_ana]->ROI[i]->deltay/2), r, g, b, 2);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int _dig = 0; _dig < GENERAL.size(); ++_dig)
|
||||
for (int i = 0; i < GENERAL[_dig]->ROI.size(); ++i)
|
||||
_zw->drawRect(GENERAL[_dig]->ROI[i]->posx, GENERAL[_dig]->ROI[i]->posy, GENERAL[_dig]->ROI[i]->deltax, GENERAL[_dig]->ROI[i]->deltay, 0, 0, (255 - _dig*100), 2);
|
||||
for (int _ana = 0; _ana < GENERAL.size(); ++_ana)
|
||||
for (int i = 0; i < GENERAL[_ana]->ROI.size(); ++i)
|
||||
{
|
||||
_zw->drawRect(GENERAL[_ana]->ROI[i]->posx, GENERAL[_ana]->ROI[i]->posy, GENERAL[_ana]->ROI[i]->deltax, GENERAL[_ana]->ROI[i]->deltay, r, g, b, 1);
|
||||
_zw->drawEllipse( (int) (GENERAL[_ana]->ROI[i]->posx + GENERAL[_ana]->ROI[i]->deltax/2), (int) (GENERAL[_ana]->ROI[i]->posy + GENERAL[_ana]->ROI[i]->deltay/2), (int) (GENERAL[_ana]->ROI[i]->deltax/2), (int) (GENERAL[_ana]->ROI[i]->deltay/2), r, g, b, 2);
|
||||
_zw->drawLine((int) (GENERAL[_ana]->ROI[i]->posx + GENERAL[_ana]->ROI[i]->deltax/2), (int) GENERAL[_ana]->ROI[i]->posy, (int) (GENERAL[_ana]->ROI[i]->posx + GENERAL[_ana]->ROI[i]->deltax/2), (int) (GENERAL[_ana]->ROI[i]->posy + GENERAL[_ana]->ROI[i]->deltay), r, g, b, 2);
|
||||
_zw->drawLine((int) GENERAL[_ana]->ROI[i]->posx, (int) (GENERAL[_ana]->ROI[i]->posy + GENERAL[_ana]->ROI[i]->deltay/2), (int) GENERAL[_ana]->ROI[i]->posx + GENERAL[_ana]->ROI[i]->deltax, (int) (GENERAL[_ana]->ROI[i]->posy + GENERAL[_ana]->ROI[i]->deltay/2), r, g, b, 2);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int _dig = 0; _dig < GENERAL.size(); ++_dig)
|
||||
for (int i = 0; i < GENERAL[_dig]->ROI.size(); ++i)
|
||||
_zw->drawRect(GENERAL[_dig]->ROI[i]->posx, GENERAL[_dig]->ROI[i]->posy, GENERAL[_dig]->ROI[i]->deltax, GENERAL[_dig]->ROI[i]->deltay, 0, 0, (255 - _dig*100), 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool ClassFlowCNNGeneral::getNetworkParameter()
|
||||
{
|
||||
if (disabled)
|
||||
@@ -546,11 +552,18 @@ bool ClassFlowCNNGeneral::getNetworkParameter()
|
||||
zwcnn = FormatFileName(zwcnn);
|
||||
ESP_LOGD(TAG, "%s", zwcnn.c_str());
|
||||
if (!tflite->LoadModel(zwcnn)) {
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Can't read model file " + cnnmodelfile);
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Can't load tflite model " + cnnmodelfile + " -> Init aborted!");
|
||||
LogFile.WriteHeapInfo("getNetworkParameter-LoadModel");
|
||||
delete tflite;
|
||||
return false;
|
||||
}
|
||||
tflite->MakeAllocate();
|
||||
|
||||
if (!tflite->MakeAllocate()) {
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Can't allocate tflite model -> Init aborted!");
|
||||
LogFile.WriteHeapInfo("getNetworkParameter-MakeAllocate");
|
||||
delete tflite;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (CNNType == AutoDetect)
|
||||
{
|
||||
@@ -601,6 +614,7 @@ bool ClassFlowCNNGeneral::getNetworkParameter()
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool ClassFlowCNNGeneral::doNeuralNetwork(string time)
|
||||
{
|
||||
if (disabled)
|
||||
@@ -612,13 +626,20 @@ bool ClassFlowCNNGeneral::doNeuralNetwork(string time)
|
||||
string zwcnn = "/sdcard" + cnnmodelfile;
|
||||
zwcnn = FormatFileName(zwcnn);
|
||||
ESP_LOGD(TAG, "%s", zwcnn.c_str());
|
||||
if (!tflite->LoadModel(zwcnn)) {
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Can't read model file " + cnnmodelfile);
|
||||
|
||||
if (!tflite->LoadModel(zwcnn)) {
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Can't load tflite model " + cnnmodelfile + " -> Exec aborted this round!");
|
||||
LogFile.WriteHeapInfo("doNeuralNetwork-LoadModel");
|
||||
delete tflite;
|
||||
return false;
|
||||
}
|
||||
tflite->MakeAllocate();
|
||||
}
|
||||
|
||||
if (!tflite->MakeAllocate()) {
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Can't allocate tfilte model -> Exec aborted this round!");
|
||||
LogFile.WriteHeapInfo("doNeuralNetwork-MakeAllocate");
|
||||
delete tflite;
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int n = 0; n < GENERAL.size(); ++n) // For each NUMBER
|
||||
{
|
||||
@@ -805,6 +826,7 @@ bool ClassFlowCNNGeneral::doNeuralNetwork(string time)
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool ClassFlowCNNGeneral::isExtendedResolution(int _number)
|
||||
{
|
||||
if (!(CNNType == Digital))
|
||||
@@ -814,7 +836,6 @@ bool ClassFlowCNNGeneral::isExtendedResolution(int _number)
|
||||
}
|
||||
|
||||
|
||||
|
||||
std::vector<HTMLInfo*> ClassFlowCNNGeneral::GetHTMLInfo()
|
||||
{
|
||||
std::vector<HTMLInfo*> result;
|
||||
@@ -856,11 +877,13 @@ std::vector<HTMLInfo*> ClassFlowCNNGeneral::GetHTMLInfo()
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
int ClassFlowCNNGeneral::getNumberGENERAL()
|
||||
{
|
||||
return GENERAL.size();
|
||||
}
|
||||
|
||||
|
||||
string ClassFlowCNNGeneral::getNameGENERAL(int _analog)
|
||||
{
|
||||
if (_analog < GENERAL.size())
|
||||
@@ -869,6 +892,7 @@ string ClassFlowCNNGeneral::getNameGENERAL(int _analog)
|
||||
return "GENERAL DOES NOT EXIST";
|
||||
}
|
||||
|
||||
|
||||
general* ClassFlowCNNGeneral::GetGENERAL(int _analog)
|
||||
{
|
||||
if (_analog < GENERAL.size())
|
||||
@@ -878,7 +902,6 @@ general* ClassFlowCNNGeneral::GetGENERAL(int _analog)
|
||||
}
|
||||
|
||||
|
||||
|
||||
void ClassFlowCNNGeneral::UpdateNameNumbers(std::vector<std::string> *_name_numbers)
|
||||
{
|
||||
for (int _dig = 0; _dig < GENERAL.size(); _dig++)
|
||||
@@ -895,6 +918,7 @@ void ClassFlowCNNGeneral::UpdateNameNumbers(std::vector<std::string> *_name_numb
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
string ClassFlowCNNGeneral::getReadoutRawString(int _analog)
|
||||
{
|
||||
string rt = "";
|
||||
|
||||
@@ -29,6 +29,8 @@ extern "C" {
|
||||
|
||||
static const char* TAG = "CTRL";
|
||||
|
||||
//#define DEBUG_DETAIL_ON
|
||||
|
||||
|
||||
std::string ClassFlowControll::doSingleStep(std::string _stepname, std::string _host){
|
||||
std::string _classname = "";
|
||||
@@ -48,17 +50,17 @@ std::string ClassFlowControll::doSingleStep(std::string _stepname, std::string _
|
||||
if ((_stepname.compare("[Analog]") == 0) || (_stepname.compare(";[Analog]") == 0)){
|
||||
_classname = "ClassFlowCNNGeneral";
|
||||
}
|
||||
#ifdef ENABLE_MQTT
|
||||
#ifdef ENABLE_MQTT
|
||||
if ((_stepname.compare("[MQTT]") == 0) || (_stepname.compare(";[MQTT]") == 0)){
|
||||
_classname = "ClassFlowMQTT";
|
||||
}
|
||||
#endif //ENABLE_MQTT
|
||||
#endif //ENABLE_MQTT
|
||||
|
||||
#ifdef ENABLE_INFLUXDB
|
||||
#ifdef ENABLE_INFLUXDB
|
||||
if ((_stepname.compare("[InfluxDB]") == 0) || (_stepname.compare(";[InfluxDB]") == 0)){
|
||||
_classname = "ClassFlowInfluxDB";
|
||||
}
|
||||
#endif //ENABLE_INFLUXDB
|
||||
#endif //ENABLE_INFLUXDB
|
||||
|
||||
for (int i = 0; i < FlowControll.size(); ++i)
|
||||
if (FlowControll[i]->name().compare(_classname) == 0){
|
||||
@@ -81,14 +83,14 @@ std::string ClassFlowControll::TranslateAktstatus(std::string _input)
|
||||
return ("Aligning");
|
||||
if (_input.compare("ClassFlowCNNGeneral") == 0)
|
||||
return ("Digitalization of ROIs");
|
||||
#ifdef ENABLE_MQTT
|
||||
if (_input.compare("ClassFlowMQTT") == 0)
|
||||
return ("Sending MQTT");
|
||||
#endif //ENABLE_MQTT
|
||||
#ifdef ENABLE_INFLUXDB
|
||||
if (_input.compare("ClassFlowInfluxDB") == 0)
|
||||
return ("Sending InfluxDB");
|
||||
#endif //ENABLE_INFLUXDB
|
||||
#ifdef ENABLE_MQTT
|
||||
if (_input.compare("ClassFlowMQTT") == 0)
|
||||
return ("Sending MQTT");
|
||||
#endif //ENABLE_MQTT
|
||||
#ifdef ENABLE_INFLUXDB
|
||||
if (_input.compare("ClassFlowInfluxDB") == 0)
|
||||
return ("Sending InfluxDB");
|
||||
#endif //ENABLE_INFLUXDB
|
||||
if (_input.compare("ClassFlowPostProcessing") == 0)
|
||||
return ("Post-Processing");
|
||||
if (_input.compare("ClassFlowWriteList") == 0)
|
||||
@@ -110,6 +112,7 @@ std::vector<HTMLInfo*> ClassFlowControll::GetAllDigital()
|
||||
return empty;
|
||||
}
|
||||
|
||||
|
||||
std::vector<HTMLInfo*> ClassFlowControll::GetAllAnalog()
|
||||
{
|
||||
if (flowanalog)
|
||||
@@ -119,6 +122,7 @@ std::vector<HTMLInfo*> ClassFlowControll::GetAllAnalog()
|
||||
return empty;
|
||||
}
|
||||
|
||||
|
||||
t_CNNType ClassFlowControll::GetTypeDigital()
|
||||
{
|
||||
if (flowdigit)
|
||||
@@ -127,6 +131,7 @@ t_CNNType ClassFlowControll::GetTypeDigital()
|
||||
return t_CNNType::None;
|
||||
}
|
||||
|
||||
|
||||
t_CNNType ClassFlowControll::GetTypeAnalog()
|
||||
{
|
||||
if (flowanalog)
|
||||
@@ -158,6 +163,7 @@ bool ClassFlowControll::StartMQTTService() {
|
||||
}
|
||||
#endif //ENABLE_MQTT
|
||||
|
||||
|
||||
void ClassFlowControll::SetInitialParameter(void)
|
||||
{
|
||||
AutoStart = false;
|
||||
@@ -171,12 +177,14 @@ void ClassFlowControll::SetInitialParameter(void)
|
||||
aktstatus = "Booting ...";
|
||||
}
|
||||
|
||||
|
||||
bool ClassFlowControll::isAutoStart(long &_intervall)
|
||||
{
|
||||
_intervall = AutoIntervall * 60 * 1000; // AutoInterval: minutes -> ms
|
||||
return AutoStart;
|
||||
}
|
||||
|
||||
|
||||
ClassFlow* ClassFlowControll::CreateClassFlow(std::string _type)
|
||||
{
|
||||
ClassFlow* cfc = NULL;
|
||||
@@ -203,14 +211,14 @@ ClassFlow* ClassFlowControll::CreateClassFlow(std::string _type)
|
||||
cfc = new ClassFlowCNNGeneral(flowalignment);
|
||||
flowdigit = (ClassFlowCNNGeneral*) cfc;
|
||||
}
|
||||
#ifdef ENABLE_MQTT
|
||||
#ifdef ENABLE_MQTT
|
||||
if (toUpper(_type).compare("[MQTT]") == 0)
|
||||
cfc = new ClassFlowMQTT(&FlowControll);
|
||||
#endif //ENABLE_MQTT
|
||||
#ifdef ENABLE_INFLUXDB
|
||||
#endif //ENABLE_MQTT
|
||||
#ifdef ENABLE_INFLUXDB
|
||||
if (toUpper(_type).compare("[INFLUXDB]") == 0)
|
||||
cfc = new ClassFlowInfluxDB(&FlowControll);
|
||||
#endif //ENABLE_INFLUXDB
|
||||
#endif //ENABLE_INFLUXDB
|
||||
if (toUpper(_type).compare("[WRITELIST]") == 0)
|
||||
cfc = new ClassFlowWriteList(&FlowControll);
|
||||
|
||||
@@ -238,6 +246,7 @@ ClassFlow* ClassFlowControll::CreateClassFlow(std::string _type)
|
||||
return cfc;
|
||||
}
|
||||
|
||||
|
||||
void ClassFlowControll::InitFlow(std::string config)
|
||||
{
|
||||
string line;
|
||||
@@ -279,14 +288,17 @@ void ClassFlowControll::InitFlow(std::string config)
|
||||
}
|
||||
|
||||
fclose(pFile);
|
||||
|
||||
}
|
||||
|
||||
std::string* ClassFlowControll::getActStatus(){
|
||||
|
||||
std::string* ClassFlowControll::getActStatus()
|
||||
{
|
||||
return &aktstatus;
|
||||
}
|
||||
|
||||
void ClassFlowControll::doFlowMakeImageOnly(string time){
|
||||
|
||||
void ClassFlowControll::doFlowMakeImageOnly(string time)
|
||||
{
|
||||
std::string zw_time;
|
||||
|
||||
for (int i = 0; i < FlowControll.size(); ++i)
|
||||
@@ -295,24 +307,25 @@ void ClassFlowControll::doFlowMakeImageOnly(string time){
|
||||
zw_time = getCurrentTimeString("%H:%M:%S");
|
||||
std::string flowStatus = TranslateAktstatus(FlowControll[i]->name());
|
||||
aktstatus = flowStatus + " (" + zw_time + ")";
|
||||
#ifdef ENABLE_MQTT
|
||||
MQTTPublish(mqttServer_getMainTopic() + "/" + "status", flowStatus, false);
|
||||
#endif //ENABLE_MQTT
|
||||
#ifdef ENABLE_MQTT
|
||||
MQTTPublish(mqttServer_getMainTopic() + "/" + "status", flowStatus, false);
|
||||
#endif //ENABLE_MQTT
|
||||
|
||||
FlowControll[i]->doFlow(time);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool ClassFlowControll::doFlow(string time)
|
||||
{
|
||||
bool result = true;
|
||||
std::string zw_time;
|
||||
int repeat = 0;
|
||||
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
LogFile.WriteHeapInfo("ClassFlowControll::doFlow - Start");
|
||||
#endif
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
LogFile.WriteHeapInfo("ClassFlowControll::doFlow - Start");
|
||||
#endif
|
||||
|
||||
/* Check if we have a valid date/time and if not restart the NTP client */
|
||||
/* if (! getTimeIsSet()) {
|
||||
@@ -328,12 +341,12 @@ bool ClassFlowControll::doFlow(string time)
|
||||
std::string flowStatus = TranslateAktstatus(FlowControll[i]->name());
|
||||
aktstatus = flowStatus + " (" + zw_time + ")";
|
||||
//LogFile.WriteToFile(ESP_LOG_INFO, TAG, aktstatus);
|
||||
#ifdef ENABLE_MQTT
|
||||
MQTTPublish(mqttServer_getMainTopic() + "/" + "status", flowStatus, false);
|
||||
#endif //ENABLE_MQTT
|
||||
#ifdef ENABLE_MQTT
|
||||
MQTTPublish(mqttServer_getMainTopic() + "/" + "status", flowStatus, false);
|
||||
#endif //ENABLE_MQTT
|
||||
|
||||
string zw = "FlowControll.doFlow - " + FlowControll[i]->name();
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
string zw = "FlowControll.doFlow - " + FlowControll[i]->name();
|
||||
LogFile.WriteHeapInfo(zw);
|
||||
#endif
|
||||
|
||||
@@ -353,18 +366,19 @@ bool ClassFlowControll::doFlow(string time)
|
||||
result = true;
|
||||
}
|
||||
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
LogFile.WriteHeapInfo("ClassFlowControll::doFlow");
|
||||
#endif
|
||||
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
LogFile.WriteHeapInfo("ClassFlowControll::doFlow");
|
||||
#endif
|
||||
}
|
||||
|
||||
zw_time = getCurrentTimeString("%H:%M:%S");
|
||||
std::string flowStatus = "Flow finished";
|
||||
aktstatus = flowStatus + " (" + zw_time + ")";
|
||||
//LogFile.WriteToFile(ESP_LOG_INFO, TAG, aktstatus);
|
||||
#ifdef ENABLE_MQTT
|
||||
MQTTPublish(mqttServer_getMainTopic() + "/" + "status", flowStatus, false);
|
||||
#endif //ENABLE_MQTT
|
||||
#ifdef ENABLE_MQTT
|
||||
MQTTPublish(mqttServer_getMainTopic() + "/" + "status", flowStatus, false);
|
||||
#endif //ENABLE_MQTT
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -434,6 +448,7 @@ string ClassFlowControll::getReadout(bool _rawvalue = false, bool _noerror = fal
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
string ClassFlowControll::GetPrevalue(std::string _number)
|
||||
{
|
||||
if (flowpostprocessing)
|
||||
@@ -444,6 +459,7 @@ string ClassFlowControll::GetPrevalue(std::string _number)
|
||||
return std::string("");
|
||||
}
|
||||
|
||||
|
||||
std::string ClassFlowControll::UpdatePrevalue(std::string _newvalue, std::string _numbers, bool _extern)
|
||||
{
|
||||
float zw;
|
||||
@@ -473,6 +489,7 @@ std::string ClassFlowControll::UpdatePrevalue(std::string _newvalue, std::string
|
||||
return std::string();
|
||||
}
|
||||
|
||||
|
||||
bool ClassFlowControll::ReadParameter(FILE* pfile, string& aktparamgraph)
|
||||
{
|
||||
std::vector<string> splitted;
|
||||
@@ -626,90 +643,119 @@ esp_err_t ClassFlowControll::GetJPGStream(std::string _fn, httpd_req_t *req)
|
||||
{
|
||||
ESP_LOGD(TAG, "ClassFlowControll::GetJPGStream %s", _fn.c_str());
|
||||
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
LogFile.WriteHeapInfo("ClassFlowControll::GetJPGStream - Start");
|
||||
#endif
|
||||
|
||||
CImageBasis *_send = NULL;
|
||||
esp_err_t result = ESP_FAIL;
|
||||
bool Dodelete = false;
|
||||
bool _sendDelete = false;
|
||||
|
||||
if (flowalignment == NULL)
|
||||
if (flowalignment == NULL)
|
||||
{
|
||||
ESP_LOGD(TAG, "Can't continue, flowalignment is NULL");
|
||||
return ESP_FAIL;
|
||||
ESP_LOGD(TAG, "ClassFloDControll::GetJPGStream: FloDalignment is not (yet) initialized. Interrupt serving!");
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
if (_fn == "alg.jpg")
|
||||
{
|
||||
_send = flowalignment->ImageBasis;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_fn == "alg_roi.jpg")
|
||||
{
|
||||
CImageBasis* _imgzw = new CImageBasis(flowalignment->ImageBasis);
|
||||
flowalignment->DrawRef(_imgzw);
|
||||
if (flowdigit) flowdigit->DrawROI(_imgzw);
|
||||
if (flowanalog) flowanalog->DrawROI(_imgzw);
|
||||
_send = _imgzw;
|
||||
Dodelete = true;
|
||||
if (_fn == "alg.jpg") {
|
||||
if (flowalignment && flowalignment->ImageBasis->ImageOkay()) {
|
||||
_send = flowalignment->ImageBasis;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::vector<HTMLInfo*> htmlinfo;
|
||||
htmlinfo = GetAllDigital();
|
||||
ESP_LOGD(TAG, "After getClassFlowControll::GetAllDigital");
|
||||
else {
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "ClassFlowControll::GetJPGStream: alg.jpg cannot be served");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
}
|
||||
else if (_fn == "alg_roi.jpg") {
|
||||
_send = new CImageBasis(flowalignment->ImageBasis);
|
||||
|
||||
for (int i = 0; i < htmlinfo.size(); ++i)
|
||||
{
|
||||
if (_fn == htmlinfo[i]->filename)
|
||||
{
|
||||
if (htmlinfo[i]->image)
|
||||
_send = htmlinfo[i]->image;
|
||||
}
|
||||
if (_fn == htmlinfo[i]->filename_org)
|
||||
{
|
||||
if (htmlinfo[i]->image_org)
|
||||
_send = htmlinfo[i]->image_org;
|
||||
}
|
||||
delete htmlinfo[i];
|
||||
if (_send->ImageOkay()) {
|
||||
if (flowalignment) flowalignment->DrawRef(_send);
|
||||
if (flowdigit) flowdigit->DrawROI(_send);
|
||||
if (flowanalog) flowanalog->DrawROI(_send);
|
||||
_sendDelete = true; // delete temporary _send element after sending
|
||||
}
|
||||
else {
|
||||
LogFile.WriteToFile(ESP_LOG_WARN, TAG, "ClassFlowControll::GetJPGStream: Not enough memory to create alg_roi.jpg -> alg.jpg is going to be served!");
|
||||
|
||||
if (flowalignment && flowalignment->ImageBasis->ImageOkay()) {
|
||||
_send = flowalignment->ImageBasis;
|
||||
}
|
||||
htmlinfo.clear();
|
||||
|
||||
if (!_send)
|
||||
{
|
||||
htmlinfo = GetAllAnalog();
|
||||
for (int i = 0; i < htmlinfo.size(); ++i)
|
||||
{
|
||||
if (_fn == htmlinfo[i]->filename)
|
||||
{
|
||||
if (htmlinfo[i]->image)
|
||||
_send = htmlinfo[i]->image;
|
||||
}
|
||||
if (_fn == htmlinfo[i]->filename_org)
|
||||
{
|
||||
if (htmlinfo[i]->image_org)
|
||||
_send = htmlinfo[i]->image_org;
|
||||
}
|
||||
delete htmlinfo[i];
|
||||
}
|
||||
htmlinfo.clear();
|
||||
|
||||
else {
|
||||
httpd_resp_send(req, NULL, 0);
|
||||
return ESP_OK;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
std::vector<HTMLInfo*> htmlinfo;
|
||||
|
||||
htmlinfo = GetAllDigital();
|
||||
ESP_LOGD(TAG, "After getClassFlowControll::GetAllDigital");
|
||||
|
||||
for (int i = 0; i < htmlinfo.size(); ++i)
|
||||
{
|
||||
if (_fn == htmlinfo[i]->filename)
|
||||
{
|
||||
if (htmlinfo[i]->image)
|
||||
_send = htmlinfo[i]->image;
|
||||
}
|
||||
|
||||
if (_fn == htmlinfo[i]->filename_org)
|
||||
{
|
||||
if (htmlinfo[i]->image_org)
|
||||
_send = htmlinfo[i]->image_org;
|
||||
}
|
||||
delete htmlinfo[i];
|
||||
}
|
||||
htmlinfo.clear();
|
||||
|
||||
if (!_send)
|
||||
{
|
||||
htmlinfo = GetAllAnalog();
|
||||
ESP_LOGD(TAG, "After getClassFlowControll::GetAllAnalog");
|
||||
|
||||
for (int i = 0; i < htmlinfo.size(); ++i)
|
||||
{
|
||||
if (_fn == htmlinfo[i]->filename)
|
||||
{
|
||||
if (htmlinfo[i]->image)
|
||||
_send = htmlinfo[i]->image;
|
||||
}
|
||||
|
||||
if (_fn == htmlinfo[i]->filename_org)
|
||||
{
|
||||
if (htmlinfo[i]->image_org)
|
||||
_send = htmlinfo[i]->image_org;
|
||||
}
|
||||
delete htmlinfo[i];
|
||||
}
|
||||
htmlinfo.clear();
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
LogFile.WriteHeapInfo("ClassFlowControll::GetJPGStream - before send");
|
||||
#endif
|
||||
|
||||
if (_send)
|
||||
{
|
||||
ESP_LOGD(TAG, "Sending file: %s ...", _fn.c_str());
|
||||
set_content_type_from_file(req, _fn.c_str());
|
||||
result = _send->SendJPGtoHTTP(req);
|
||||
ESP_LOGD(TAG, "File sending complete");
|
||||
/* Respond with an empty chunk to signal HTTP response completion */
|
||||
httpd_resp_send_chunk(req, NULL, 0);
|
||||
ESP_LOGD(TAG, "File sending complete");
|
||||
|
||||
if (_sendDelete)
|
||||
delete _send;
|
||||
|
||||
_send = NULL;
|
||||
}
|
||||
|
||||
if (Dodelete)
|
||||
{
|
||||
delete _send;
|
||||
}
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
LogFile.WriteHeapInfo("ClassFlowControll::GetJPGStream - done");
|
||||
#endif
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -720,6 +766,7 @@ string ClassFlowControll::getNumbersName()
|
||||
return flowpostprocessing->getNumbersName();
|
||||
}
|
||||
|
||||
|
||||
string ClassFlowControll::getJSON()
|
||||
{
|
||||
return flowpostprocessing->GetJSON();
|
||||
|
||||
@@ -52,7 +52,7 @@ void ClassFlowMakeImage::SetInitialParameter(void)
|
||||
SaveAllFiles = false;
|
||||
disabled = false;
|
||||
FixedExposure = false;
|
||||
namerawimage = "/sdcard/img_tmp/raw.jpg";
|
||||
namerawimage = "/sdcard/img_tmp/raw.jpg";
|
||||
}
|
||||
|
||||
|
||||
@@ -63,6 +63,7 @@ ClassFlowMakeImage::ClassFlowMakeImage(std::vector<ClassFlow*>* lfc) : ClassFlow
|
||||
SetInitialParameter();
|
||||
}
|
||||
|
||||
|
||||
bool ClassFlowMakeImage::ReadParameter(FILE* pfile, string& aktparamgraph)
|
||||
{
|
||||
std::vector<string> splitted;
|
||||
@@ -171,6 +172,7 @@ bool ClassFlowMakeImage::ReadParameter(FILE* pfile, string& aktparamgraph)
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
string ClassFlowMakeImage::getHTMLSingleStep(string host)
|
||||
{
|
||||
string result;
|
||||
@@ -178,43 +180,45 @@ string ClassFlowMakeImage::getHTMLSingleStep(string host)
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
bool ClassFlowMakeImage::doFlow(string zwtime)
|
||||
{
|
||||
string logPath = CreateLogFolder(zwtime);
|
||||
|
||||
int flash_duration = (int) (waitbeforepicture * 1000);
|
||||
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
LogFile.WriteHeapInfo("ClassFlowMakeImage::doFlow - Before takePictureWithFlash");
|
||||
#endif
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
LogFile.WriteHeapInfo("ClassFlowMakeImage::doFlow - Before takePictureWithFlash");
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef WIFITURNOFF
|
||||
esp_wifi_stop(); // to save power usage and
|
||||
#endif
|
||||
#ifdef WIFITURNOFF
|
||||
esp_wifi_stop(); // to save power usage and
|
||||
#endif
|
||||
|
||||
takePictureWithFlash(flash_duration);
|
||||
|
||||
#ifdef WIFITURNOFF
|
||||
esp_wifi_start();
|
||||
#endif
|
||||
#ifdef WIFITURNOFF
|
||||
esp_wifi_start();
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
LogFile.WriteHeapInfo("ClassFlowMakeImage::doFlow - After takePictureWithFlash");
|
||||
#endif
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
LogFile.WriteHeapInfo("ClassFlowMakeImage::doFlow - After takePictureWithFlash");
|
||||
#endif
|
||||
|
||||
LogImage(logPath, "raw", NULL, NULL, zwtime, rawImage);
|
||||
|
||||
RemoveOldLogs();
|
||||
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
LogFile.WriteHeapInfo("ClassFlowMakeImage::doFlow - After RemoveOldLogs");
|
||||
#endif
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
LogFile.WriteHeapInfo("ClassFlowMakeImage::doFlow - After RemoveOldLogs");
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
esp_err_t ClassFlowMakeImage::SendRawJPG(httpd_req_t *req)
|
||||
{
|
||||
int flash_duration = (int) (waitbeforepicture * 1000);
|
||||
|
||||
Reference in New Issue
Block a user