Files
IoTManager/src/classes/IoTItem.cpp
biver 057a95a434 Корректируем определение ulong на unsigned long
валидатору кода в VS не нравилось короткое название
2023-03-26 13:26:57 +03:00

336 lines
13 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#include "utils/JsonUtils.h"
#include "utils/SerialPrint.h"
#include "classes/IoTItem.h"
#include "WsServer.h"
#include "ESPConfiguration.h"
#include "EventsAndOrders.h"
IoTItem::IoTItem(const String& parameters) {
jsonRead(parameters, F("int"), _interval, false);
if (_interval == 0) enableDoByInt = false; // выключаем использование периодического выполнения в модуле
if (_interval > 0) _interval = _interval * 1000; // если int положителен, то считаем, что получены секунды
if (_interval < 0) _interval = _interval * -1; // если int отрицательный, то миллисекунды
jsonRead(parameters, F("subtype"), _subtype, false);
jsonRead(parameters, F("id"), _id);
if (!jsonRead(parameters, F("multiply"), _multiply, false)) _multiply = 1;
if (!jsonRead(parameters, F("plus"), _plus, false)) _plus = 0;
if (!jsonRead(parameters, F("round"), _round, false)) _round = -1;
if (!jsonRead(parameters, F("global"), _global, false)) _global = false;
String map;
jsonRead(parameters, F("map"), map, false);
if (map != "") {
_map1 = selectFromMarkerToMarker(map, ",", 0).toInt();
_map2 = selectFromMarkerToMarker(map, ",", 1).toInt();
_map3 = selectFromMarkerToMarker(map, ",", 2).toInt();
_map4 = selectFromMarkerToMarker(map, ",", 3).toInt();
} else
_map1 = _map2 = _map3 = _map4 = 0;
String valAsStr = "";
if (jsonRead(parameters, F("val"), valAsStr, false)) // значение переменной или датчика при инициализации если есть в конфигурации
setValue(valAsStr, false);
jsonRead(parameters, F("needSave"), _needSave, false);
if (_needSave && jsonRead(valuesFlashJson, _id, valAsStr, false)) // пробуем достать из сохранения значение элемента, если указано, что нужно сохранять
setValue(valAsStr, false);
}
void IoTItem::loop() {
if (enableDoByInt) {
currentMillis = millis();
difference = currentMillis - prevMillis;
if (difference >= _interval) {
prevMillis = millis();
this->doByInterval();
}
}
}
String IoTItem::getValue() {
if (value.isDecimal) {
return getRoundValue();
} else
return value.valS;
}
long IoTItem::getInterval() { return _interval; }
bool IoTItem::isGlobal() { return _global; }
void IoTItem::setValue(const String& valStr, bool genEvent) {
value.isDecimal = isDigitDotCommaStr(valStr);
if (value.isDecimal) {
value.valD = valStr.toFloat();
getRoundValue();
} else {
value.valS = valStr;
}
setValue(value, genEvent);
}
void IoTItem::setValue(const IoTValue& Value, bool genEvent) {
value = Value;
if (value.isDecimal) {
regEvent(value.valD, "", false, genEvent);
} else {
regEvent(value.valS, "", false, genEvent);
}
}
// метод отправки из модуля дополнительных json полей виджета в приложение и веб интерфейс,
// необходимый для изменения виджетов "на лету" из модуля
void IoTItem::sendSubWidgetsValues(String& id, String& json) {
publishJsonMqtt(id, json);
publishJsonWs(id, json);
}
// когда событие случилось
void IoTItem::regEvent(const String& value, const String& consoleInfo, bool error, bool genEvent) {
if (_needSave) {
jsonWriteStr_(valuesFlashJson, _id, value);
needSaveValues = true;
}
publishStatusMqtt(_id, value);
publishStatusWs(_id, value);
// SerialPrint("i", "Sensor", consoleInfo + " '" + _id + "' data: " + value + "'");
if (genEvent) {
generateEvent(_id, value);
// распространяем событие через хуки
for (std::list<IoTItem*>::iterator it = IoTItems.begin(); it != IoTItems.end(); ++it) {
(*it)->onRegEvent(this);
}
// отправка события другим устройствам в сети если не было ошибки
if (_global && !error) {
String json = "{}";
jsonWriteStr_(json, "id", _id);
jsonWriteStr_(json, "val", value);
jsonWriteInt_(json, "int", _interval / 1000);
publishEvent(_id, json);
SerialPrint("i", F("<=MQTT"), "Broadcast event: " + json);
}
}
}
String IoTItem::getRoundValue() {
if (!value.isDecimal) return value.valS;
if (_round >= 0 && _round <= 6) {
int sot = _round ? pow(10, (int)_round) : 1;
value.valD = round(value.valD * sot) / sot;
char buf[15];
sprintf(buf, ("%1." + (String)_round + "f").c_str(), value.valD);
value.valS = (String)buf;
return value.valS;
} else {
value.valS = (String)value.valD;
return value.valS;
}
}
void IoTItem::regEvent(float regvalue, const String& consoleInfo, bool error, bool genEvent) {
value.valD = regvalue;
if (_multiply) value.valD = value.valD * _multiply;
if (_plus) value.valD = value.valD + _plus;
if (_map1 != _map2) value.valD = map(value.valD, _map1, _map2, _map3, _map4);
regEvent(getRoundValue(), consoleInfo, error, genEvent);
}
void IoTItem::doByInterval() {}
IoTValue IoTItem::execute(String command, std::vector<IoTValue>& param) { return {}; }
String IoTItem::getSubtype() {
return _subtype;
}
int IoTItem::getIntFromNet() {
return _intFromNet;
}
void IoTItem::getNetEvent(String& event) {
event = "{}";
jsonWriteStr_(event, "id", _id);
jsonWriteStr_(event, "val", getValue());
jsonWriteInt_(event, "int", _interval / 1000);
}
void IoTItem::setIntFromNet(int interval) {
_intFromNet = interval;
}
void IoTItem::checkIntFromNet() {
// проверяем элемент на доверие данным.
if (_intFromNet >= 0) {
// если время жизни истекло, то удаляем элемент чуть позже на следующем такте loop
// если это было уведомление не об ошибке или начале работы, то сообщаем, что сетевое событие давно не приходило
if (_intFromNet == 0 && _id.indexOf("onError") == -1 && _id.indexOf("onStart") == -1 && _id.indexOf("onInit") == -1) {
SerialPrint("E", _id, "The new data did not come from the network. The level of trust is low.", _id);
}
_intFromNet--;
}
}
// хуки для системных событий (должны начинаться с "on")
void IoTItem::onRegEvent(IoTItem* item) {}
void IoTItem::onMqttRecive(String& topic, String& msg) {}
void IoTItem::onMqttWsAppConnectEvent() {}
void IoTItem::onModuleOrder(String& key, String& value) {}
// делаем доступным модулям отправку сообщений в телеграм
void IoTItem::sendTelegramMsg(bool often, String msg) {}
// методы для графиков (будет упрощено)
void IoTItem::publishValue() {}
void IoTItem::clearValue() {}
void IoTItem::setPublishDestination(int publishType, int wsNum){};
void IoTItem::clearHistory() {}
void IoTItem::setTodayDate() {}
String IoTItem::getID() {
return _id;
};
bool IoTItem::isStrInID(const String& str) {
return _id.indexOf(str) != -1;
}
void IoTItem::setInterval(long interval) {
_interval = interval;
}
IoTGpio* IoTItem::getGpioDriver() {
return nullptr;
}
IoTItem* IoTItem::getRtcDriver() {
return nullptr;
}
unsigned long IoTItem::getRtcUnixTime() {
return 0;
}
// сетевое общение====================================================================================================================================
// externalVariable::externalVariable(const String& parameters) : IoTItem(parameters) {
// prevMillis = millis(); // запоминаем текущее значение таймера для выполения doByInterval после int сек
// iAmLocal = false; // указываем, что это сущность прилетела из сети
// //Serial.printf("Call from externalVariable: parameters %s %d\n", parameters.c_str(), _interval);
// }
// externalVariable::~externalVariable() {
// Serial.printf("Call from ~externalVariable: Im dead\n");
// }
// void externalVariable::doByInterval() { // для данного класса doByInterval+int выполняет роль счетчика обратного отсчета до уничтожения
// iAmDead = true;
// }
//=========================================================================================================================================
IoTItem* myIoTItem;
// поиск элемента модуля в существующей конфигурации
IoTItem* findIoTItem(const String& name) {
if (name == "") return nullptr;
for (std::list<IoTItem*>::iterator it = IoTItems.begin(); it != IoTItems.end(); ++it) {
if ((*it)->getID() == name) return *it;
}
return nullptr;
}
// поиск элемента модуля в существующей конфигурации по части имени
IoTItem* findIoTItemByPartOfName(const String& partName) {
if (partName == "") return nullptr;
for (std::list<IoTItem*>::iterator it = IoTItems.begin(); it != IoTItems.end(); ++it) {
if ((*it)->isStrInID(partName)) return *it;
}
return nullptr;
}
// поиск плюс получение значения
String getItemValue(const String& name) {
IoTItem* tmp = findIoTItem(name);
if (tmp)
return tmp->getValue();
else
return "";
}
// существует ли айтем
bool isItemExist(const String& name) {
IoTItem* tmp = findIoTItem(name);
if (tmp)
return true;
else
return false;
}
// создаем временную копию элемента из сети на основе события
IoTItem* createItemFromNet(const String& itemId, const String& value, int interval) {
String jsonStr = "{\"id\":\"";
jsonStr += itemId;
jsonStr += "\",\"val\":\"";
jsonStr += value;
jsonStr += "\",\"int\":";
jsonStr += interval;
jsonStr += "}";
return createItemFromNet(jsonStr);
}
// создаем временную копию элемента из сети на основе события
IoTItem* createItemFromNet(const String& msgFromNet) {
IoTItem* tmpp = new IoTItem(msgFromNet);
//Serial.println("vvvvvvvvvvv " + msgFromNet + " " + (String)tmpp->getInterval());
if (tmpp->getInterval()) tmpp->setIntFromNet(tmpp->getInterval() / 1000 + 5);
tmpp->iAmLocal = false;
IoTItems.push_back(tmpp);
generateEvent(tmpp->getID(), tmpp->getValue());
return tmpp;
}
void analyzeMsgFromNet(const String& msg, String altId) {
if (!jsonRead(msg, F("id"), altId, altId == "")) return; // ничего не предпринимаем, если ошибка и altId = "", вообще данная конструкция нужна для совместимости с форматом данных 3 версией
IoTItem* itemExist = findIoTItem(altId);
if (itemExist) {
String valAsStr = msg;
jsonRead(msg, F("val"), valAsStr, false);
long interval = 0;
jsonRead(msg, F("int"), interval, false);
itemExist->setInterval(interval); // устанавливаем такой же интервал как на источнике события
itemExist->setValue(valAsStr, false); // только регистрируем изменения в интерфейсе без создания цикла сетевых событий
if (interval) itemExist->setIntFromNet(interval + 5); // если пришедший интервал =0, значит не нужно контролировать доверие, иначе даем фору в 5 сек
generateEvent(altId, valAsStr);
} else {
// временно зафиксируем данные в базе, если локально элемент отсутствует
createItemFromNet(msg);
//Serial.println("ffffffffff " + msg + " altId=" + altId);
}
}
//StaticJsonDocument<JSON_BUFFER_SIZE> docForExport;
// StaticJsonDocument<JSON_BUFFER_SIZE>* getLocalItemsAsJSON() {
// docForExport.clear();
// for (std::list<IoTItem*>::iterator it = IoTItems.begin(); it != IoTItems.end(); ++it) {
// if ((*it)->iAmLocal) docForExport[(*it)->getID()] = (*it)->getValue();
// }
// return &docForExport;
// }