Skip to content
Snippets Groups Projects
shsensor.cpp 13.7 KiB
Newer Older
  • Learn to ignore specific revisions
  • #include "shsensor.h"
    
    Peter Altenbernd's avatar
    Peter Altenbernd committed
    #include <QTimer>
    #include <QtGlobal>
    
    int SHsensor::m_sensors = 0;
    bool SHsensor::m_testing = false;
    
    QDateTime * SHsensor::m_testTime = nullptr;
    
    Peter Altenbernd's avatar
    Peter Altenbernd committed
    SHsensor * SHsensor::S[SHNodeIdSize * SHSensorIdSize] = {nullptr};
    
    QMap<QString,int> SHsensor::NID;
    
    Peter Altenbernd's avatar
    Peter Altenbernd committed
    QNetworkAccessManager * SHsensor::m_tasmota = nullptr;
    
    Peter Altenbernd's avatar
    Peter Altenbernd committed
    
    
    Peter Altenbernd's avatar
    Peter Altenbernd committed
    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;
    
    
    Peter Altenbernd's avatar
    Peter Altenbernd committed
    
    
    /*
     * Konstruktor
     */
    
    Peter Altenbernd's avatar
    Peter Altenbernd committed
    SHsensor::SHsensor(/* QString name,*/ int nid, SHsensor::SensorType type, QString url) :
    
    Peter Altenbernd's avatar
    Peter Altenbernd committed
        m_nodeID(nid),
    
        // m_name(name),
    
    Peter Altenbernd's avatar
    Peter Altenbernd committed
        m_url(url),
    
        m_type(type),
    
    Peter Altenbernd's avatar
    Peter Altenbernd committed
        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) {
    
    Peter Altenbernd's avatar
    Peter Altenbernd committed
        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");
    
    Peter Altenbernd's avatar
    Peter Altenbernd committed
    
        S[m_pos] = this;
    
    
        m_error = noError;
    
    Peter Altenbernd's avatar
    Peter Altenbernd committed
    }
    
    /*
     *
     */
    SHsensor::~SHsensor()
    {
        S[m_pos] = nullptr;
    
        m_sensors--;
    }
    
    
    Peter Altenbernd's avatar
    Peter Altenbernd committed
    int SHsensor::pos() const
    {
        return m_pos;
    }
    
    
    
    Peter Altenbernd's avatar
    Peter Altenbernd committed
    /*
     *
     */
    bool SHsensor::tasmota() const
    {
        return (m_url != "");
    }
    
    
    
    QDateTime SHsensor::timestamp() const
    
    Peter Altenbernd's avatar
    Peter Altenbernd committed
    {
        return m_timestamp;
    }
    
    
    
    
    /*
     *
     */
    QString SHsensor::value() const
    {
    
        if (isError())
            return ("F" + QString::number((int) m_error));
        else
            return m_value;
    
    Peter Altenbernd's avatar
    Peter Altenbernd committed
    }
    
    
    
    
    /*
     *
     */
    QString SHsensor::unit() const
    {
        return m_unit;
    }
    
    
    
    
    /*
     *
     */
    QString SHsensor::name() const
    {
    
        return SHsensor::name(m_nodeID);
    
    Peter Altenbernd's avatar
    Peter Altenbernd committed
    }
    
    
    
    
    
    /*
     *
     */
    void SHsensor::checkTimeStamp()
    {
        // qDebug() << "SHsensorBase::checkTimeStamp()";
    
    
        QDateTime time = m_timestamp.addSecs(CONTROL_INTERVALL);
    
    Peter Altenbernd's avatar
    Peter Altenbernd committed
    
        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;
        }
    
    }
    
    
    
    
    
    Peter Altenbernd's avatar
    Peter Altenbernd committed
    /*
     *
     */
    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);
    }
    
    
    
    
    
    Peter Altenbernd's avatar
    Peter Altenbernd committed
    /*
     *
     */
    void SHsensor::poll() const
    {
        if (not tasmota())
            return;
    
        QString cmd = "cm?cmnd=Status0";
    
        QString urlRequest = m_url + "/" + cmd;
    
        QNetworkRequest request;
    
    
        // qDebug() << "polling tasmota" << name();
    
    Peter Altenbernd's avatar
    Peter Altenbernd committed
    
        request.setUrl(QUrl(urlRequest));
        m_tasmota->get(request);
    
    }
    
    
    
    
    
    Peter Altenbernd's avatar
    Peter Altenbernd committed
    /*
     *
     */
    void SHsensor::setValue(const QString &payload)
    {
    
        // qDebug() << "setValue::setValue(nid=" << m_nodeID << "sid=" << m_sensorID << "payl=" << payload << ")";
    
    Peter Altenbernd's avatar
    Peter Altenbernd committed
    
    
        // qDebug() <<  "-> 1 isError" <<  isError();
        // qDebug() << m_errorIndicated << m_error << m_previousError;
    
    Peter Altenbernd's avatar
    Peter Altenbernd committed
    
    
        QString payl = payload;
    
    Peter Altenbernd's avatar
    Peter Altenbernd committed
    
    
        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::none:
            case SHsensor::Switch:
            case SHsensor::Switch2:
                break;
    
            default:
                throw std::invalid_argument("SHsensor::SHsensor: type out of range");
            }
    
    Peter Altenbernd's avatar
    Peter Altenbernd committed
        }
    
    
    //    if (tasmota() and m_type == SHsensor::tempSensor) // wird nicht mehr gebraucht
    //        setNervousTemp(payl);
    //    else
    
    Peter Altenbernd's avatar
    Peter Altenbernd committed
    }
    
    
    
    
    /*
     *
     */
    bool SHsensor::isTesting()
    {
        return m_testing;
    }
    
    
    /*
     *
     */
    void SHsensor::setTesting()
    {
    
    void SHsensor::setTestTime(const QDateTime &time)
    
    Peter Altenbernd's avatar
    Peter Altenbernd committed
    {
        assert(m_testing);
    
        *m_testTime = time;
    }
    
    
    
    
    
    /*
     *
     */
    
    QDateTime SHsensor::currentTime()
    
    Peter Altenbernd's avatar
    Peter Altenbernd committed
    {
        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;
    
    }
    
    
    
    Peter Altenbernd's avatar
    Peter Altenbernd committed
    /*
     *
     */
    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");
    
    Peter Altenbernd's avatar
    Peter Altenbernd committed
    
        for (auto s : S)
    
            if (s != nullptr) {
    
    Peter Altenbernd's avatar
    Peter Altenbernd committed
                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;
                }
    
    
    Peter Altenbernd's avatar
    Peter Altenbernd committed
    
        qDebug() << line;
    
        // SHsensor::displayMem("SHsensor::printALL ENDE");
    
        errcnt_total += errcnt;
    
    
        SHsensor::setErrorInfo(errcnt_total, errcnt, errsolar);
    
    Peter Altenbernd's avatar
    Peter Altenbernd committed
    /*
     *
     */
    void SHsensor::pollTasmota() {
    
        // SHsensor::displayMem("SHsensor::pollTasmota");
    
    
    Peter Altenbernd's avatar
    Peter Altenbernd committed
        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");
    
    
    Peter Altenbernd's avatar
    Peter Altenbernd committed
    /*
     *
     */
    void SHsensor::setValue(int nid, int sid, const QString &payl)
    {
    
    Peter Altenbernd's avatar
    Peter Altenbernd committed
        // qDebug() << "static SHsensor::setValue(nid=" << nid << "sid=" << sid << "payl=" << payl << ")";
    
    Peter Altenbernd's avatar
    Peter Altenbernd committed
    
        int pos = hash(nid, sid);
    
    
    Peter Altenbernd's avatar
    Peter Altenbernd committed
        // qDebug() << "pos" << pos << "S[pos]" << S[pos];
    
    
    Peter Altenbernd's avatar
    Peter Altenbernd committed
        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];
    
    }
    
    
    Peter Altenbernd's avatar
    Peter Altenbernd committed
    
    
    Peter Altenbernd's avatar
    Peter Altenbernd committed
    void SHsensor::initStatics(QNetworkAccessManager *tasmota)
    
    Peter Altenbernd's avatar
    Peter Altenbernd committed
        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;
        }
    
    }
    
    
    Peter Altenbernd's avatar
    Peter Altenbernd committed
    /*
     *
     */
    int SHsensor::nid(QString name)
    {
        if (NID.contains(name))
            return NID[name];
        else
            return -1;
    }
    
    
    
    
    SHsensor::ErrorType SHsensor::error() const
    {
        return m_error;
    }
    
    
    Peter Altenbernd's avatar
    Peter Altenbernd committed
    
    
    
    /*
     *
     */
    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;
    }
    
    Peter Altenbernd's avatar
    Peter Altenbernd committed
    void SHsensor::setSolarInfo(int newBrutto, int newNetto, const QStringList &newInfo)
    
    Peter Altenbernd's avatar
    Peter Altenbernd committed
        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 = {
    
    Peter Altenbernd's avatar
    Peter Altenbernd committed
            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;
    }