mirror of
https://github.com/IoTManagerProject/IoTManager.git
synced 2026-03-27 14:42:18 +03:00
262 Logging added, working version
This commit is contained in:
@@ -43,7 +43,7 @@
|
||||
},
|
||||
{
|
||||
"type": "h4",
|
||||
"title": "LittleFS version: 261"
|
||||
"title": "LittleFS version: 262"
|
||||
},
|
||||
{
|
||||
"type": "hr"
|
||||
@@ -116,7 +116,7 @@
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"title": "<div style='margin-top:10px;margin-bottom:10px;'><font color='black'><p style='border: 1px solid #DCDCDC; border-radius: 3px; background-color: #F5F5F5; padding: 10px;'>После любого изменения таблицы элементов, включая удаление/добавление строк, необходимо нажать кнопку <b>Сохранить таблицу</b></p></font></div>"
|
||||
"title": "<div style='margin-top:10px;margin-bottom:10px;'><font color='black'><p style='border: 1px solid #DCDCDC; border-radius: 3px; background-color: #F5F5F5; padding: 10px;'>После любого изменения таблицы элементов, включая удаление/добавление строк, необходимо нажать кнопку <b>СОХРАНИТЬ ТАБЛИЦУ</b></p></font></div>"
|
||||
},
|
||||
{
|
||||
"type": "h2",
|
||||
@@ -153,7 +153,7 @@
|
||||
"class": "btn btn-block btn-default"
|
||||
},
|
||||
{
|
||||
"type": "link",
|
||||
"type": "button",
|
||||
"title": "Очистить логи сенсоров",
|
||||
"action": "/set?cleanlog",
|
||||
"class": "btn btn-block btn-default"
|
||||
|
||||
@@ -108,7 +108,7 @@ class LineParsing {
|
||||
_descr.replace("%ver%", String(FIRMWARE_VERSION));
|
||||
_descr.replace("%name%", jsonReadStr(configSetupJson, F("name")));
|
||||
|
||||
createWidgetClass(_descr, _page, _order, _file, _key);
|
||||
createWidget(_descr, _page, _order, _file, _key);
|
||||
}
|
||||
|
||||
//jsonWriteStr(configOptionJson, _key + "_pin", _pin);
|
||||
@@ -188,13 +188,15 @@ class LineParsing {
|
||||
return str.substring(p1 + 1, p2);
|
||||
}
|
||||
|
||||
void createWidgetClass(String descr, String page, String order, String filename, String topic) {
|
||||
void createWidget(String descr, String page, String order, String filename, String topic) {
|
||||
if (filename != "na") {
|
||||
String buf = "{}";
|
||||
if (!loadWidgetClass(filename, buf)) {
|
||||
if (!loadWidget(filename, buf)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(filename.indexOf("chart") != -1) jsonWriteStr(buf, "maxCount", _cnt);
|
||||
|
||||
jsonWriteStr(buf, "page", page);
|
||||
jsonWriteStr(buf, "order", order);
|
||||
jsonWriteStr(buf, "descr", descr);
|
||||
@@ -208,8 +210,8 @@ class LineParsing {
|
||||
}
|
||||
}
|
||||
|
||||
bool loadWidgetClass(const String& filename, String& buf) {
|
||||
buf = readFile(getWidgetFileClass(filename), 2048);
|
||||
bool loadWidget(const String& filename, String& buf) {
|
||||
buf = readFile(getWidgetFile(filename), 2048);
|
||||
bool res = !(buf == "Failed" || buf == "Large");
|
||||
if (!res) {
|
||||
//SerialPrint("[E]","module","on load" + filename);
|
||||
@@ -217,18 +219,9 @@ class LineParsing {
|
||||
return res;
|
||||
}
|
||||
|
||||
const String getWidgetFileClass(const String& name) {
|
||||
const String getWidgetFile(const String& name) {
|
||||
return "/widgets/" + name + ".json";
|
||||
}
|
||||
|
||||
//String jsonWriteStr1(String& json, String name, String value) {
|
||||
// DynamicJsonBuffer jsonBuffer;
|
||||
// JsonObject& root = jsonBuffer.parseObject(json);
|
||||
// root[name] = value;
|
||||
// json = "";
|
||||
// root.printTo(json);
|
||||
// return json;
|
||||
//}
|
||||
};
|
||||
|
||||
extern LineParsing myLineParsing;
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
//
|
||||
#ifdef ESP8266
|
||||
#define FIRMWARE_NAME "esp8266-iotm"
|
||||
#define FIRMWARE_VERSION 261
|
||||
#define FIRMWARE_VERSION 262
|
||||
#endif
|
||||
#ifdef ESP32
|
||||
#define FIRMWARE_NAME "esp32-iotm"
|
||||
@@ -44,8 +44,8 @@
|
||||
//#define MDNS_ENABLED
|
||||
//#define WEBSOCKET_ENABLED
|
||||
//#define LAYOUT_IN_RAM
|
||||
#define UDP_ENABLED
|
||||
#define SSDP_ENABLED
|
||||
//#define UDP_ENABLED
|
||||
//#define SSDP_ENABLED
|
||||
|
||||
//
|
||||
// Sensors enable/disable
|
||||
@@ -111,20 +111,6 @@ enum NotAsyncActions {
|
||||
do_LAST,
|
||||
};
|
||||
|
||||
enum ErrorType_t {
|
||||
ET_NONE,
|
||||
ET_FUNCTION,
|
||||
ET_MODULE,
|
||||
ET_SYSTEM
|
||||
};
|
||||
|
||||
enum ErrorLevel_t {
|
||||
EL_NONE,
|
||||
EL_INFO,
|
||||
EL_WARNING,
|
||||
EL_ERROR
|
||||
};
|
||||
|
||||
enum LedStatus_t {
|
||||
LED_OFF,
|
||||
LED_ON,
|
||||
|
||||
@@ -79,8 +79,8 @@ extern int lastVersion;
|
||||
// Logging
|
||||
extern void logging();
|
||||
extern void deleteOldDate(String filename, size_t max_lines, String date_to_add);
|
||||
extern void clean_log_date();
|
||||
extern void choose_log_date_and_send();
|
||||
|
||||
|
||||
|
||||
// Main
|
||||
extern void setChipId();
|
||||
|
||||
@@ -10,7 +10,7 @@ typedef std::vector<LoggingClass> MyLoggingVector;
|
||||
class LoggingClass {
|
||||
public:
|
||||
|
||||
LoggingClass(unsigned long period, unsigned int maxPoints, String key);
|
||||
LoggingClass(unsigned long period, unsigned int maxPoints, String loggingValueKey, String key);
|
||||
~LoggingClass();
|
||||
|
||||
void loop();
|
||||
@@ -21,9 +21,14 @@ class LoggingClass {
|
||||
unsigned long prevMillis;
|
||||
unsigned long _period;
|
||||
unsigned int _maxPoints;
|
||||
String _loggingValueKey;
|
||||
String _key;
|
||||
|
||||
void addNewDelOldData(const String filename, size_t maxPoints, String payload);
|
||||
};
|
||||
|
||||
extern MyLoggingVector* myLogging;
|
||||
|
||||
extern void choose_log_date_and_send();
|
||||
extern void sendLogData(String file, String topic);
|
||||
extern void clean_log_date();
|
||||
|
||||
19
src/Init.cpp
19
src/Init.cpp
@@ -1,7 +1,8 @@
|
||||
#include "Global.h"
|
||||
#include "Init.h"
|
||||
#include "Cmd.h"
|
||||
|
||||
#include "Cmd.h"
|
||||
#include "Global.h"
|
||||
#include "items/LoggingClass.h"
|
||||
|
||||
void loadConfig() {
|
||||
configSetupJson = readFile("config.json", 4096);
|
||||
@@ -25,11 +26,14 @@ void all_init() {
|
||||
}
|
||||
|
||||
void Device_init() {
|
||||
sensorReadingMap10sec = "";
|
||||
dallasEnterCounter = -1;
|
||||
if (myLogging != nullptr) {
|
||||
myLogging->clear();
|
||||
}
|
||||
logging_value_names_list = "";
|
||||
|
||||
|
||||
sensorReadingMap10sec = "";
|
||||
dallasEnterCounter = -1;
|
||||
|
||||
//logging_value_names_list = "";
|
||||
//enter_to_logging_counter = LOG1 - 1;
|
||||
//analog_value_names_list = "";
|
||||
//enter_to_analog_counter = 0;
|
||||
@@ -59,7 +63,6 @@ void Device_init() {
|
||||
removeFile(String("layout.txt"));
|
||||
#endif
|
||||
|
||||
|
||||
fileCmdExecute(String(DEVICE_CONFIG_FILE));
|
||||
//outcoming_date();
|
||||
}
|
||||
@@ -79,8 +82,6 @@ void uptime_init() {
|
||||
nullptr, true);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void handle_uptime() {
|
||||
jsonWriteStr(configSetupJson, "uptime", timeNow->getUptime());
|
||||
}
|
||||
|
||||
150
src/Logging.cpp
150
src/Logging.cpp
@@ -1,150 +0,0 @@
|
||||
#include "Global.h"
|
||||
#include <LITTLEFS.h>
|
||||
|
||||
void sendLogData(String file, String topic);
|
||||
|
||||
|
||||
#ifdef LOGGING_ENABLED
|
||||
//===============================================Логирование============================================================
|
||||
//logging temp1 1 10 Температура Датчики 2
|
||||
/*void logging2() {
|
||||
String value_name = sCmd.next();
|
||||
String period_min = sCmd.next();
|
||||
String maxCount = sCmd.next();
|
||||
String widget_name = sCmd.next();
|
||||
widget_name.replace("#", " ");
|
||||
String page_name = sCmd.next();
|
||||
String page_number = sCmd.next();
|
||||
logging_value_names_list += value_name + ",";
|
||||
enter_to_logging_counter++; //считаем количество входов в эту функцию
|
||||
jsonWriteStr(configOptionJson, value_name + "_c", maxCount); //создаем в файловой системе переменную количества точек на графике с отметкой _c что значит count
|
||||
|
||||
//создаем график в приложении с топиком _ch /prefix/3234045-1589487/value_name_ch
|
||||
createChart(widget_name, page_name, page_number, "chart", value_name + "_ch", maxCount);
|
||||
|
||||
if (enter_to_logging_counter == LOG1) {
|
||||
ts.add(
|
||||
LOG1, period_min.toInt() * 1000 * 60, [&](void*) {
|
||||
String tmp_buf_1 = selectFromMarkerToMarker(logging_value_names_list, ",", 0);
|
||||
deleteOldDate("log." + tmp_buf_1 + ".txt", jsonReadInt(configOptionJson, tmp_buf_1 + "_c"), jsonReadStr(configLiveJson, tmp_buf_1));
|
||||
SerialPrint("I","module","logging for " + tmp_buf_1 + " done");
|
||||
},
|
||||
nullptr, false);
|
||||
}
|
||||
if (enter_to_logging_counter == LOG2) {
|
||||
ts.add(
|
||||
LOG2, period_min.toInt() * 1000 * 60, [&](void*) {
|
||||
String tmp_buf_2 = selectFromMarkerToMarker(logging_value_names_list, ",", 1);
|
||||
deleteOldDate("log." + tmp_buf_2 + ".txt", jsonReadInt(configOptionJson, tmp_buf_2 + "_c"), jsonReadStr(configLiveJson, tmp_buf_2));
|
||||
SerialPrint("I","module","logging for " + tmp_buf_2 + " done");
|
||||
},
|
||||
nullptr, false);
|
||||
}
|
||||
if (enter_to_logging_counter == LOG3) {
|
||||
ts.add(
|
||||
LOG3, period_min.toInt() * 1000 * 60, [&](void*) {
|
||||
String tmp_buf_3 = selectFromMarkerToMarker(logging_value_names_list, ",", 2);
|
||||
deleteOldDate("log." + tmp_buf_3 + ".txt", jsonReadInt(configOptionJson, tmp_buf_3 + "_c"), jsonReadStr(configLiveJson, tmp_buf_3));
|
||||
SerialPrint("I","module","logging for " + tmp_buf_3 + " done");
|
||||
},
|
||||
nullptr, false);
|
||||
}
|
||||
if (enter_to_logging_counter == LOG4) {
|
||||
ts.add(
|
||||
LOG4, period_min.toInt() * 1000 * 60, [&](void*) {
|
||||
String tmp_buf_4 = selectFromMarkerToMarker(logging_value_names_list, ",", 3);
|
||||
deleteOldDate("log." + tmp_buf_4 + ".txt", jsonReadInt(configOptionJson, tmp_buf_4 + "_c"), jsonReadStr(configLiveJson, tmp_buf_4));
|
||||
SerialPrint("I","module","logging for " + tmp_buf_4 + " done");
|
||||
},
|
||||
nullptr, false);
|
||||
}
|
||||
if (enter_to_logging_counter == LOG5) {
|
||||
ts.add(
|
||||
LOG5, period_min.toInt() * 1000 * 60, [&](void*) {
|
||||
String tmp_buf_5 = selectFromMarkerToMarker(logging_value_names_list, ",", 4);
|
||||
deleteOldDate("log." + tmp_buf_5 + ".txt", jsonReadInt(configOptionJson, tmp_buf_5 + "_c"), jsonReadStr(configLiveJson, tmp_buf_5));
|
||||
SerialPrint("I","module","logging for " + tmp_buf_5 + " done");
|
||||
},
|
||||
nullptr, false);
|
||||
}
|
||||
}*/
|
||||
|
||||
/*
|
||||
* Удаление стрых данных и запись новых
|
||||
*/
|
||||
/*void deleteOldDate(const String filename, size_t max_lines_cnt, String payload) {
|
||||
String log_date = readFile(filename, 5120);
|
||||
size_t lines_cnt = itemsCount(log_date, "\r\n");
|
||||
|
||||
SerialPrint("I","module","log " + filename + " (" + String(lines_cnt, DEC) + ")");
|
||||
|
||||
if ((lines_cnt > max_lines_cnt + 1) || !lines_cnt) {
|
||||
removeFile(filename);
|
||||
lines_cnt = 0;
|
||||
}
|
||||
|
||||
if (lines_cnt > max_lines_cnt) {
|
||||
log_date = deleteBeforeDelimiter(log_date, "\r\n");
|
||||
if (timeNow->hasTimeSynced()) {
|
||||
log_date += timeNow->getTimeUnix() + " " + payload + "\r\n";
|
||||
writeFile(filename, log_date);
|
||||
}
|
||||
} else {
|
||||
if (timeNow->hasTimeSynced()) {
|
||||
addFileLn(filename, timeNow->getTimeUnix() + " " + payload);
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
//=========================================Выбор какие данные отправлять==================================================================
|
||||
void choose_log_date_and_send() {
|
||||
String all_line = logging_value_names_list;
|
||||
while (all_line.length() != 0) {
|
||||
String tmp = selectToMarker(all_line, ",");
|
||||
sendLogData("log." + tmp + ".txt", tmp + "_ch");
|
||||
all_line = deleteBeforeDelimiter(all_line, ",");
|
||||
}
|
||||
}
|
||||
//=========================================Отправка данных===================================================================================
|
||||
void sendLogData(String file, String topic) {
|
||||
String log_date = readFile(file, 5120);
|
||||
if (log_date != "failed") {
|
||||
log_date.replace("\r\n", "\n");
|
||||
log_date.replace("\r", "\n");
|
||||
String buf = "{}";
|
||||
String json_array;
|
||||
String unix_time;
|
||||
String value;
|
||||
while (log_date.length()) {
|
||||
String tmp = selectToMarker(log_date, "\n");
|
||||
log_date = deleteBeforeDelimiter(log_date, "\n");
|
||||
unix_time = selectToMarker(tmp, " ");
|
||||
jsonWriteInt(buf, "x", unix_time.toInt());
|
||||
value = deleteBeforeDelimiter(tmp, " ");
|
||||
jsonWriteFloat(buf, "y1", value.toFloat());
|
||||
if (log_date.length() < 3) {
|
||||
json_array += buf;
|
||||
} else {
|
||||
json_array += buf + ",";
|
||||
}
|
||||
buf = "{}";
|
||||
}
|
||||
unix_time = "";
|
||||
value = "";
|
||||
log_date = "";
|
||||
json_array = "{\"status\":[" + json_array + "]}";
|
||||
SerialPrint("I","module",json_array);
|
||||
|
||||
publishChart(topic, json_array);
|
||||
}
|
||||
}
|
||||
|
||||
void clean_log_date() {
|
||||
String all_line = logging_value_names_list;
|
||||
while (all_line.length()) {
|
||||
String tmp = selectToMarker(all_line, ",");
|
||||
removeFile("log." + tmp + ".txt");
|
||||
all_line = deleteBeforeDelimiter(all_line, ",");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -1,7 +1,7 @@
|
||||
#include "MqttClient.h"
|
||||
|
||||
#include <LittleFS.h>
|
||||
|
||||
#include "items/LoggingClass.h"
|
||||
#include "Class/NotAsync.h"
|
||||
#include "Global.h"
|
||||
#include "Init.h"
|
||||
|
||||
@@ -20,10 +20,10 @@ void removeFile(const String& filename) {
|
||||
String path = filepath(filename);
|
||||
if (LittleFS.exists(path)) {
|
||||
if (!LittleFS.remove(path)) {
|
||||
SerialPrint("[E]","Files","remove " + path);
|
||||
SerialPrint("I","Files","remove " + path);
|
||||
}
|
||||
} else {
|
||||
SerialPrint("I","Files","not exist" + path);
|
||||
SerialPrint("E","Files","not exist" + path);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#include "Web.h"
|
||||
|
||||
#include "items/LoggingClass.h"
|
||||
#include "Class/NotAsync.h"
|
||||
#include "Global.h"
|
||||
#include "Init.h"
|
||||
|
||||
@@ -6,9 +6,10 @@
|
||||
#include "Global.h"
|
||||
#include "ItemsCmd.h"
|
||||
|
||||
LoggingClass::LoggingClass(unsigned long period, unsigned int maxPoints, String key) {
|
||||
LoggingClass::LoggingClass(unsigned long period, unsigned int maxPoints, String loggingValueKey, String key) {
|
||||
_period = period * 1000;
|
||||
_maxPoints = maxPoints;
|
||||
_loggingValueKey = loggingValueKey;
|
||||
_key = key;
|
||||
}
|
||||
|
||||
@@ -19,7 +20,7 @@ void LoggingClass::loop() {
|
||||
unsigned long difference = currentMillis - prevMillis;
|
||||
if (difference >= _period) {
|
||||
prevMillis = millis();
|
||||
addNewDelOldData("log." + _key + ".txt", _maxPoints, jsonReadStr(configLiveJson, _key));
|
||||
addNewDelOldData("logs/" + _key + ".txt", _maxPoints, jsonReadStr(configLiveJson, _loggingValueKey));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,7 +34,7 @@ void LoggingClass::addNewDelOldData(const String filename, size_t maxPoints, Str
|
||||
removeFile(filename);
|
||||
lines_cnt = 0;
|
||||
}
|
||||
|
||||
|
||||
if (payload != "") {
|
||||
if (lines_cnt > maxPoints) {
|
||||
logData = deleteBeforeDelimiter(logData, "\r\n");
|
||||
@@ -53,13 +54,67 @@ MyLoggingVector* myLogging = nullptr;
|
||||
|
||||
void logging() {
|
||||
myLineParsing.update();
|
||||
String value = myLineParsing.gvalue();
|
||||
String loggingValueKey = myLineParsing.gvalue();
|
||||
String key = myLineParsing.gkey();
|
||||
String interv = myLineParsing.gint();
|
||||
String maxcnt = myLineParsing.gmaxcnt();
|
||||
myLineParsing.clear();
|
||||
|
||||
logging_value_names_list += key + ",";
|
||||
|
||||
static bool firstTime = true;
|
||||
if (firstTime) myLogging = new MyLoggingVector();
|
||||
firstTime = false;
|
||||
myLogging->push_back(LoggingClass(interv.toInt(), maxcnt.toInt(), value));
|
||||
myLogging->push_back(LoggingClass(interv.toInt(), maxcnt.toInt(), loggingValueKey, key));
|
||||
}
|
||||
|
||||
void choose_log_date_and_send() {
|
||||
String all_line = logging_value_names_list;
|
||||
while (all_line.length() != 0) {
|
||||
String tmp = selectToMarker(all_line, ",");
|
||||
sendLogData("logs/" + tmp + ".txt", tmp);
|
||||
all_line = deleteBeforeDelimiter(all_line, ",");
|
||||
}
|
||||
}
|
||||
|
||||
void sendLogData(String file, String topic) {
|
||||
String log_date = readFile(file, 5120);
|
||||
if (log_date != "failed") {
|
||||
log_date.replace("\r\n", "\n");
|
||||
log_date.replace("\r", "\n");
|
||||
String buf = "{}";
|
||||
String json_array;
|
||||
String unix_time;
|
||||
String value;
|
||||
while (log_date.length()) {
|
||||
String tmp = selectToMarker(log_date, "\n");
|
||||
log_date = deleteBeforeDelimiter(log_date, "\n");
|
||||
unix_time = selectToMarker(tmp, " ");
|
||||
jsonWriteInt(buf, "x", unix_time.toInt());
|
||||
value = deleteBeforeDelimiter(tmp, " ");
|
||||
jsonWriteFloat(buf, "y1", value.toFloat());
|
||||
if (log_date.length() < 3) {
|
||||
json_array += buf;
|
||||
} else {
|
||||
json_array += buf + ",";
|
||||
}
|
||||
buf = "{}";
|
||||
}
|
||||
unix_time = "";
|
||||
value = "";
|
||||
log_date = "";
|
||||
json_array = "{\"status\":[" + json_array + "]}";
|
||||
//SerialPrint("I", "module", json_array);
|
||||
|
||||
publishChart(topic, json_array);
|
||||
}
|
||||
}
|
||||
|
||||
void clean_log_date() {
|
||||
auto dir = LittleFS.openDir("logs");
|
||||
while (dir.next()) {
|
||||
String fname = dir.fileName();
|
||||
SerialPrint("I", "System", fname);
|
||||
removeFile("logs/" + fname);
|
||||
}
|
||||
}
|
||||
|
||||
18
src/main.cpp
18
src/main.cpp
@@ -2,6 +2,7 @@
|
||||
#include <SSDP.h>
|
||||
|
||||
#include "BufferExecute.h"
|
||||
#include "Bus.h"
|
||||
#include "Class/CallBackTest.h"
|
||||
#include "Class/NotAsync.h"
|
||||
#include "Class/ScenarioClass.h"
|
||||
@@ -9,13 +10,12 @@
|
||||
#include "Global.h"
|
||||
#include "Init.h"
|
||||
#include "ItemsList.h"
|
||||
#include "RemoteOrdersUdp.h"
|
||||
#include "Utils/StatUtils.h"
|
||||
#include "Utils/Timings.h"
|
||||
#include "Utils/WebUtils.h"
|
||||
#include "items/ButtonInClass.h"
|
||||
#include "items/LoggingClass.h"
|
||||
#include "RemoteOrdersUdp.h"
|
||||
#include "Bus.h"
|
||||
|
||||
void not_async_actions();
|
||||
|
||||
@@ -74,11 +74,11 @@ void setup() {
|
||||
|
||||
SerialPrint("I", "Stat", "Stat Init");
|
||||
initSt();
|
||||
|
||||
#ifdef UDP_ENABLED
|
||||
SerialPrint("I","UDP","Udp Init");
|
||||
|
||||
#ifdef UDP_ENABLED
|
||||
SerialPrint("I", "UDP", "Udp Init");
|
||||
asyncUdpInit();
|
||||
#endif
|
||||
#endif
|
||||
|
||||
SerialPrint("I", "Bus", "Bus Init");
|
||||
busInit();
|
||||
@@ -99,11 +99,11 @@ void setup() {
|
||||
just_load = false;
|
||||
initialized = true; //this second POST makes the data to be processed (you don't need to connect as "keep-alive" for that to work)
|
||||
|
||||
//myLogging = new MyLoggingVector();
|
||||
//myLogging->push_back(LoggingClass(30000, 10, "analog-adc-1"));
|
||||
//myLogging->push_back(LoggingClass(10000, 1, "10 sec"));
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
void loop() {
|
||||
if (!initialized) {
|
||||
return;
|
||||
|
||||
Reference in New Issue
Block a user