-
Peter Altenbernd authoredPeter Altenbernd authored
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
shsensor.cpp 13.68 KiB
#include "shsensor.h"
#include <QTimer>
#include <QtGlobal>
int SHsensor::m_sensors = 0;
bool SHsensor::m_testing = false;
QDateTime * SHsensor::m_testTime = nullptr;
SHsensor * SHsensor::S[SHNodeIdSize * SHSensorIdSize] = {nullptr};
QMap<QString,int> SHsensor::NID;
QNetworkAccessManager * SHsensor::m_tasmota = nullptr;
QString SHsensor::m_brutto = NOVALUE;
QString SHsensor::m_netto = NOVALUE;
QStringList SHsensor::m_info = {};
QString SHsensor::m_errcnt_total = NOVALUE;
QString SHsensor::m_errcnt_current = NOVALUE;
QString SHsensor::m_err_solar = NOVALUE;
/*
* Konstruktor
*/
SHsensor::SHsensor(/* QString name,*/ int nid, SHsensor::SensorType type, QString url) :
m_nodeID(nid),
// m_name(name),
m_url(url),
m_type(type),
m_value(NOVALUE),
m_timestamp(SHsensor::currentTime())
{
if (nid < 0 or nid >= SHNodeIdSize)
throw std::invalid_argument("SHsensor::SHsensor: nid out of range");
switch (m_type) {
case SHsensor::none:
m_unit = "?";
m_sensorID = SID_DUMMY;
break;
case SHsensor::tempSensor:
m_unit = "C";
m_sensorID = SID_TEMP;
break;
case SHsensor::powerSensor:
case SHsensor::lightSensor:
m_unit = "W";
m_sensorID = SID_POWER;
break;
case SHsensor::Switch:
m_unit = "";
m_sensorID = SID_RELAY;
break;
case SHsensor::Switch2:
m_unit = "";
m_sensorID = SID_RELAY2;
break;
default:
throw std::invalid_argument("SHsensor::SHsensor: type out of range");
}
// Kontrolle der Zeitstempel
// QTimer *timer = new QTimer(this);
// connect(timer, &QTimer::timeout, this, &SHsensor::checkTimeStamp);
// timer->start(CONTROL_INTERVALL * 1000); // ms
// Zähler für Konsistenz
m_sensors++;
// Hinzufügen:
m_pos = hash(m_nodeID, m_sensorID);
// qDebug() << m_name << pos;
if (S[m_pos] != nullptr)
throw std::invalid_argument("SHsensor::SHsensor: nid=" + QString::number(m_nodeID).toStdString() + "/ sid=" + QString::number(m_sensorID).toStdString() + " not unique");
S[m_pos] = this;
m_error = noError;
}
/*
*
*/
SHsensor::~SHsensor()
{
S[m_pos] = nullptr;
m_sensors--;
}
/*
*
*/
int SHsensor::pos() const
{
return m_pos;
}
/*
*
*/
bool SHsensor::tasmota() const
{
return (m_url != "");
}
/*
*
*/
int SHsensor::sensorID() const
{
return m_sensorID;
}
/*
*
*/
int SHsensor::nodeID() const
{
return m_nodeID;
}
/*
*
*/
QDateTime SHsensor::timestamp() const
{
return m_timestamp;
}
/*
*
*/
QString SHsensor::value() const
{
if (isError())
return ("F" + QString::number((int) m_error));
else
return m_value;
}
/*
*
*/
QString SHsensor::unit() const
{
return m_unit;
}
/*
*
*/
QString SHsensor::name() const
{
return SHsensor::name(m_nodeID);
}
/*
*
*/
void SHsensor::checkTimeStamp()
{
// qDebug() << "SHsensorBase::checkTimeStamp()";
QDateTime time = m_timestamp.addSecs(CONTROL_INTERVALL);
if (SHsensor::currentTime() > time) {
setValue(NOVALUE); // wird zu F1 error
}
}
/*
* Nur für Tasmota und Temp
*/
void SHsensor::setNervousTemp(const QString &payload)
{
const int noAvg = 30;
if (payload != NOVALUE) {
float temp = payload.toFloat();
if (T.size() >= noAvg) {
Tsum -= T[0];
T.pop_front();
}
T.push_back(temp);
Tsum += temp;
// Durchschnitt berechnen
int Tint = Tsum/T.size() * 10; // auf eine Nachkommastelle beschränken
m_value = QString::number(Tint / 10.0);
} else {
if (T.size() > 0) {
Tsum -= T[0];
T.pop_front();
}
m_value = NOVALUE;
}
}
/*
*
*/
int SHsensor::sensors()
{
return m_sensors;
}
/*
*
*/
static QString errorTxt(SHsensor::ErrorType error) {
switch (error) {
case SHsensor::ErrorType::noError:
return "";
break;
case SHsensor::ErrorType::noValueError:
return "Kein Wert";
break;
case SHsensor::ErrorType::stupidValueError:
return "Fehlerhafter Wert";
break;
case SHsensor::ErrorType::switchError:
return "Schalter reagiert nicht";
break;
}
return "Unbekannter Fehler";
}
/*
*
*/
QString SHsensor::errorTxt() const
{
return ::errorTxt(m_error);
}
/*
*
*/
QString SHsensor::prevErrorTxt() const
{
return ::errorTxt(m_previousError);
}
/*
*
*/
void SHsensor::poll() const
{
if (not tasmota())
return;
QString cmd = "cm?cmnd=Status0";
QString urlRequest = m_url + "/" + cmd;
QNetworkRequest request;
// qDebug() << "polling tasmota" << name();
request.setUrl(QUrl(urlRequest));
m_tasmota->get(request);
}
/*
*
*/
void SHsensor::setValue(const QString &payload)
{
// qDebug() << "setValue::setValue(nid=" << m_nodeID << "sid=" << m_sensorID << "payl=" << payload << ")";
// qDebug() << "-> 1 isError" << isError();
// qDebug() << m_errorIndicated << m_error << m_previousError;
QString payl = payload;
if (payl != NOVALUE) {
m_timestamp = SHsensor::currentTime();
resetError(noValueError);
} else {
setError(noValueError);
}
// Konsistenz-Check für bestimmte Sensoren:
if (payl != NOVALUE) {
float fvalue;
switch (m_type) {
case SHsensor::tempSensor:
fvalue = payl.toFloat();
if (fvalue < 0.0 or fvalue > 45.0) {
setError(stupidValueError);
payl = NOVALUE;
} else {
resetError(stupidValueError);
}
break;
case SHsensor::powerSensor:
fvalue = payl.toFloat();
if (fvalue < 0.0) {
setError(stupidValueError);
payl = NOVALUE;
} else {
resetError(stupidValueError);
}
break;
case SHsensor::lightSensor:
case SHsensor::none:
case SHsensor::Switch:
case SHsensor::Switch2:
break;
default:
throw std::invalid_argument("SHsensor::SHsensor: type out of range");
}
}
// if (tasmota() and m_type == SHsensor::tempSensor) // wird nicht mehr gebraucht
// setNervousTemp(payl);
// else
m_value = payl;
}
/*
*
*/
bool SHsensor::isTesting()
{
return m_testing;
}
/*
*
*/
void SHsensor::setTesting()
{
m_testTime = new QDateTime;
m_testing = true;
}
/*
*
*/
void SHsensor::setTestTime(const QDateTime &time)
{
assert(m_testing);
*m_testTime = time;
}
/*
*
*/
QDateTime SHsensor::currentTime()
{
if (m_testing) {
assert(m_testTime != nullptr);
return *m_testTime;
} else {
return QDateTime::currentDateTime();
}
}
/*
*
*/
void SHsensor::displayMem(QString comment)
{
QString command = "top -b -o +%MEM | head -n 10 | grep SHserver";
// QString command = "ssh pi@192.168.178.23 \"top -b -o +%MEM | head -n 10 | grep SHserver\"";
FILE* fp;
char* result = NULL;
size_t len = 0;
fflush(NULL);
fp = popen(command.toStdString().c_str(), "r");
if (fp == NULL) {
qDebug() << "Cannot execute command:" << command;
return;
}
QString qs;
while(getline(&result, &len, fp) != -1) {
// fputs(result, stdout);
qs += result;
}
// qDebug() << "result=" << result;
// qDebug() << "qs=" << qs;
free(result);
fflush(fp);
if (pclose(fp) != 0) {
perror("Cannot close stream.\n");
}
QStringList qsl = qs.split(' '/* QT6, Qt::SkipEmptyParts*/);
// qDebug() << qsl;
static int index = 0;
static int cnt = 0;
while (qsl[index] == "" or cnt < 5) {
index++;
if (qsl[index] != "")
cnt++;
}
static int prevsize = 0;
int memsize = qsl[index].toInt();
if (memsize > prevsize)
qDebug() << "#" << comment << "memsize" << memsize << ">" << prevsize;
else
qDebug() << "#" << comment << "memsize" << memsize;
prevsize = memsize;
}
/*
*
*/
void SHsensor::checkTimeStampALL()
{
for (auto s : S)
if (s != nullptr)
s->checkTimeStamp();
}
/*
* static
*/
int SHsensor::hash(int nid, int sid)
{
if (nid < 0 or nid >= SHNodeIdSize or sid < 0 or sid >= SHSensorIdSize) {
qDebug() << "hash out of range" << nid << sid;
return -1;
}
// assert(nid >= 0);
// assert(nid < SHNodeIdSize);
// assert(sid >= 0);
// assert(sid < SHSensorIdSize);
return nid * SHSensorIdSize + sid;
}
/*
*
*/
void SHsensor::printALL() {
static int errcnt_total = 0;
int errcnt = 0;
bool errsolar = false;
// SHsensor::displayMem("SHsensor::printALL");
QString line = QDate::currentDate().toString("dd.MM.yyyy") + " " + QTime::currentTime().toString("hh:mm:ss");
for (auto s : S)
if (s != nullptr) {
line = line + " " + s->name() + ":" + s->value() + s->unit();
// qDebug() << s->name() << s->unit() << s->value() << s->timestamp();
if (s->isError()) {
errcnt++;
if (s->type() == SHsensor::SensorType::lightSensor)
errsolar = true;
}
}
qDebug() << line;
// SHsensor::displayMem("SHsensor::printALL ENDE");
errcnt_total += errcnt;
SHsensor::setErrorInfo(errcnt_total, errcnt, errsolar);
}
/*
*
*/
void SHsensor::pollTasmota() {
// SHsensor::displayMem("SHsensor::pollTasmota");
static QVector<SHsensor*> T; // nur Tasmota und keine doppelten Namen
static bool firstRun = true;
if (firstRun) { // T einmalig erstellen
for (auto s : S)
if (s != nullptr and s->tasmota()) {
bool found = false;
for (auto t : T)
if (t->name() == s->name())
found = true;
if (not found)
T.push_back(s);
}
firstRun = false;
}
for (auto s : T)
if (s != nullptr)
s->poll();
// SHsensor::displayMem("SHsensor::pollTasmota ENDE");
}
/*
*
*/
void SHsensor::setValue(int nid, int sid, const QString &payl)
{
// qDebug() << "static SHsensor::setValue(nid=" << nid << "sid=" << sid << "payl=" << payl << ")";
int pos = hash(nid, sid);
// qDebug() << "pos" << pos << "S[pos]" << S[pos];
if (pos != -1 and S[pos] != nullptr) {
// qDebug() << "call setValue" << nid << sid;
S[pos]->setValue(payl);
}
}
/*
*
*/
QString SHsensor::getValue(int nid, int sid)
{
int pos = hash(nid, sid);
if (pos == -1 or S[pos] == nullptr)
return "ERRnoValue";
else
return S[pos]->value();
}
/*
*
*/
QString SHsensor::name(int nid)
{
static QString N[SHNodeIdSize] = {
"Gateway",
"HW_40", // Heißwasser Stufe 1
"Kamin", // große Heizung im Wohnzimmer, Stufe 2 gibt es nicht mehr
"Sonne", // Lichtsensor
"HW_60", // Heißwasser Stufe 2
"Tür", // Fußboden-Heizung im Wohnzimmer
"Klima", // Klimaanlage
"Bad oben", // Heizung Bad oben
"Wasch", // Waschmaschine
"Wohn", // Thermometer im Wohnzimmer
"Repeater2", // Repeater
"eCar", // E-Auto
"T Bad unten", // Thermometer Bad unten
"T Bad oben", // Thermometer Bad oben
"WandLR", // Wohnzimmer-Wand links+rechts
"WandM", // Wohnzimmer-Wand mitte
"Bad unten", // Heizung Bad unten
"Strom", // Zentraler Strommesser
"Dummy" // zum Testen neuer Knoten -- immer ganz hinten
};
if (nid < 0 or nid >= SHNodeIdSize)
return "#ERR-NAME#";
return N[nid];
}
/*
*
*/
void SHsensor::initStatics(QNetworkAccessManager *tasmota)
{
m_tasmota = tasmota;
for (int i=0; i < SHNodeIdSize; i++) {
if (NID.contains(name(i)))
throw std::invalid_argument("ERROR double use of " + name(i).toStdString());
NID[name(i)] = i;
}
}
/*
*
*/
int SHsensor::nid(QString name)
{
if (NID.contains(name))
return NID[name];
else
return -1;
}
/*
*
*/
SHsensor::ErrorType SHsensor::error() const
{
return m_error;
}
/*
*
*/
void SHsensor::setError(ErrorType newError)
{
if (not isError()) { // keine Aktion falls es bereits einen Fehler gibt
m_error = newError;
m_errorTime = m_timestamp;
}
}
/*
*
*/
void SHsensor::resetError(ErrorType oldError)
{
if (m_error == oldError) {
m_previousError = m_error;
m_previousErrorTime = m_errorTime;
m_error = noError;
}
}
/*
*
*/
bool SHsensor::isError() const
{
return (m_error != noError);
}
/*
*
*/
SHsensor::SensorType SHsensor::type() const
{
return m_type;
}
/*
*
*/
void SHsensor::setSolarInfo(int newBrutto, int newNetto, const QStringList &newInfo)
{
SHsensor::m_brutto = QString::number(newBrutto);
SHsensor::m_netto = QString::number(newNetto);
SHsensor::m_info = newInfo;
}
/*
*
*/
void SHsensor::setErrorInfo(int errcnt_total, int errcnt_current, bool err_solar)
{
SHsensor::m_errcnt_total = QString::number(errcnt_total);
SHsensor::m_errcnt_current = QString::number(errcnt_current);
if (err_solar)
SHsensor::m_err_solar = "ERROR";
else
SHsensor::m_err_solar = "OK";
}
/*
*
*/
QStringList SHsensor::getInfo() {
QStringList qsl = {
SHsensor::m_brutto, SHsensor::m_netto, SHsensor::m_info.join(";"), // solar
SHsensor::m_errcnt_total, SHsensor::m_errcnt_current, SHsensor::m_err_solar // errors
};
return qsl;
}
/*
*
*/
const QString &SHsensor::url() const
{
return m_url;
}