-
Peter Altenbernd authoredPeter Altenbernd authored
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
server.cpp 16.74 KiB
#include "server.h"
#include "SHmessage.h"
#include "webSocketMsg.h"
#include <QDebug>
#include <QHostAddress>
#include <QTimer>
#include <unistd.h>
/*
* Konstruktor
*/
Server::Server(QString TCPhostName, QObject *parent) :
QObject(parent),
m_gatewayHostName(TCPhostName),
m_gatewayTCPsocket(this)
{
qDebug() << "Starting ... ";
// Verbindung zum MySensors Controller:
m_gatewayTCPsocket.connectToHost(QHostAddress(m_gatewayHostName), 5003);
connect(&m_gatewayTCPsocket, &QTcpSocket::readyRead,
this, &Server::onReadyReadSensorValueTCP);
qDebug() << "TCPsocket" << m_gatewayHostName << "Port 5003" ;
SHactuator::setSocket(&m_gatewayTCPsocket);
// HTTP Requests für Tasmota:
m_tasmota = new QNetworkAccessManager();
// m_tasmota->setTransferTimeout(1500); // ms -- ab Qt 5.15
connect(m_tasmota, &QNetworkAccessManager::finished,
this, &Server::tasmotaReply);
SHsensor::initStatics(m_tasmota); // Map für alle Namen und Tasmota Manager
// serielle Schnittstelle ansehen und starten:
// startSerial();
// Sensoren:
// temparature
SHsensor *t_wrom = new SHsensor(NodeIdTempWR, SHsensor::tempSensor, "http://192.168.178.72");
SHsensor *t_lrom = new SHsensor(NodeIDLivingroom, SHsensor::tempSensor, "http://192.168.178.102");
SHsensor *t_bath = new SHsensor(NodeIDBadUntenT, SHsensor::tempSensor, "http://192.168.178.100");
// SHsensor *t_dummy = new SHsensor(NodeIdDummy, SHsensor::tempSensor, "http://192.168.178.72");
// producer
// SHsensor *solar = new SHsensor(NodeIdLightSensor, SHsensor::lightSensor);
SHsensor *central = new SHsensor(NodeIDCentral, SHsensor::lightSensor, "http://192.168.178.142");
// consumer
// SHsensor *wash = new SHsensor(NodeIDWashMachine, SHsensor::powerSensor, "http://192.168.178.105");
// SHsensor *ecar = new SHsensor(NodeIDeCar, SHsensor::powerSensor);
// SHsensor *airc = new SHsensor(NodeIdAirconditioner, SHsensor::powerSensor, "http://192.168.178.49");
// Repeater:
// SHsensor *dummy = new SHsensor("Dummy", NodeIdDummy, SHsensor::none); addAllSensors einbeziehen
// SHsensor *repeat2 = new SHsensor(NodeIDRepeater2, SHsensor::none);
//
// Schalter + Controller
//
// Bad;8;1; Off;22.5;18.5;07:15;21:00
// Arbeit;7;1;Off;22.5;18.5;07:15;21:00
// Wohn;5;1; Off;22.5;18.5;07:15;23:00
// Kamin;2;1; Off;22.5;18.5;07:15;23:00
// Bad unten:
SHactuator *sBath300 = new SHactuator(NodeIDBadUnten, 320, SHactuator::SensorType::Switch, "http://192.168.178.93");
HeatControl *controlBath300 = new HeatControl(t_bath, sBath300, 22.5, 18.5, QTime(7,10), QTime(21,0));
// Arbeitszimmer
SHactuator *sWrom750 = new SHactuator(NodeIdSolarheatWR, 745, SHactuator::SensorType::Switch, "http://192.168.178.24");
HeatControl * controlWRom750 = new HeatControl(t_wrom, sWrom750, 22.5, 18.5, QTime(7,10), QTime(21,0));
// Wohn- und Esszimmer
SHactuator *sLRom1000 = new SHactuator(NodeIdSolarheat, 1115, SHactuator::SensorType::Switch, "http://192.168.178.68");
HeatControl *controlKamn1000 = new HeatControl(t_lrom, sLRom1000, 22.5, 18.5, QTime(7,12), QTime(23,0));
SHactuator *sWall900 = new SHactuator(NodeIDWallLR, 910, SHactuator::SensorType::Switch, "http://192.168.178.30");
HeatControl *controlWall900 = new HeatControl(t_lrom, sWall900, 22.5, 18.5, QTime(7,14), QTime(23,0));
SHactuator *sWall450 = new SHactuator(NodeIDWallM, 455, SHactuator::SensorType::Switch, "http://192.168.178.96");
HeatControl *controlWall450 = new HeatControl(t_lrom, sWall450, 22.5, 18.5, QTime(7,16), QTime(23,0));
SHactuator *sFlor150 = new SHactuator(NodeIdSolarheatFloor, 155, SHactuator::SensorType::Switch, "http://192.168.178.63");
HeatControl *controlFlor150 = new HeatControl(t_lrom, sFlor150, 22.5, 18.5, QTime(7,16), QTime(23,0));
// Heißwasser:
SHactuator *sHW_60_2500 = new SHactuator(NodeIdHW_60, 2500, SHactuator::SensorType::Switch, "http://192.168.178.122");
SHactuator *sHW_40_2500 = new SHactuator(NodeIdHW_40, 2500, SHactuator::SensorType::Switch, "http://192.168.178.74");
HotwaterControl *controlHW_60_2500 = new HotwaterControl(sHW_60_2500);
HotwaterControl *controlHW_40_2500 = new HotwaterControl(sHW_40_2500);
// solar controllers
m_solarControl =
new SolarControl(
central, /* solar, */
/* {wash, ecar, airc}, */
{controlHW_40_2500, controlHW_60_2500, controlKamn1000, controlWall900, controlWRom750, controlWall450, controlBath300, controlFlor150} // absteigende Sortierung bzw. Priorisierung
);
// Ping: TODO
// einlesen:
HeatControl::readCSV();
// Timer starten:
startTimers();
// WebSocketServer
startWebSocketServer();
// SHsensor::displayMem("Server::Server ENDE");
}
/*
*
*/
Server::Server() // private: nur zum Testen
{
SHsensor::initStatics(nullptr);
}
/*
*
*/
void Server::startTimers()
{
// Timer zum regelmäßigen Übersenden der Werte an den / die Clients:
QTimer *submitTimer = new QTimer;
connect(submitTimer, &QTimer::timeout, this, &Server::showSensorValues);
submitTimer->start(17 * 1000); // ms
// Kontrolle der Zeitstempel:
QTimer *timeStampTimer = new QTimer(this);
connect(timeStampTimer, &QTimer::timeout, this, &SHsensor::checkTimeStampALL);
timeStampTimer->start(SHsensor::CONTROL_INTERVALL * 1000); // ms
// heatControl Timer:
QTimer *heatControlTimer = new QTimer(this);
connect(heatControlTimer, &QTimer::timeout, this, &HeatControl::processALL);
heatControlTimer->start(61 * 1000); // ms
// SolarControl Timer:
QTimer *SolarControlTimer = new QTimer(this);
connect(SolarControlTimer, &QTimer::timeout, m_solarControl, &SolarControl::process);
SolarControlTimer->start(61 * 1000); // ms
// SHsensor::displayMem("Server::startTimers ENDE");
}
/*
*
*/
void Server::startWebSocketServer()
{
for (int i=10; i>=0; i--) { // wait some time for previous TCP connection to get started
qDebug() << "Count down" << i;
sleep(1);
}
m_webSocketServer = new QWebSocketServer(QStringLiteral("SH Server"), QWebSocketServer::NonSecureMode, this);
qDebug() << "webSocketServer Interface started";
const quint16 port = 1235;
if (m_webSocketServer->listen(QHostAddress::Any, port)) {
qDebug() << "webSocketServer listening on port " + QString::number(port);
connect(m_webSocketServer, &QWebSocketServer::newConnection,
this, &Server::onNewClientConnection);
// connect(m_webSocketServer, &QWebSocketServer::closed,
// this, &Server::closed);
// mutex.unlock();
} else {
qDebug() << "ERROR: m_pWebSocketServer->listen " + QString::number(port);
}
// SHsensor::displayMem("Server::startWebSocketServer ENDE");
}
/*
*
*/
void Server::sendToClient(const QJsonObject & jsonObj)
{
mutex.lock();
QWebSocket *pClient = nullptr;
if (m_webSocketClients.size() > 0)
pClient = qobject_cast<QWebSocket *>(sender());
// find matching client
for (int i=0; i < m_webSocketClients.size(); i++)
if (m_webSocketClients[i] == pClient) {
// send the json message
QString message = QJsonDocument(jsonObj).toJson(QJsonDocument::Compact);
if (m_webSocketClients[i]) {
// qDebug() << "Sending to " + QString::number(application->getClientID());
m_webSocketClients[i]->sendTextMessage(message);
} else
qDebug() << "Sending ERROR message '" + message + "' to " << i;
break;
}
mutex.unlock();
}
#ifdef OLD
/*
*
*/
void Server::startSerial() {
m_serial = nullptr; // weil im Fehlerfall diese Methode erneut aufgerufen wird (siehe connect unten)
const char blankString[] = QT_TRANSLATE_NOOP("SettingsDialog", "N/A");
const auto infos = QSerialPortInfo::availablePorts();
QString port;
// Infos einsammeln:
for (const QSerialPortInfo &info : infos) {
QStringList list;
QString description = info.description();
QString manufacturer = info.manufacturer();
QString serialNumber = info.serialNumber();
list << info.portName()
<< (!description.isEmpty() ? description : blankString)
<< (!manufacturer.isEmpty() ? manufacturer : blankString)
<< (!serialNumber.isEmpty() ? serialNumber : blankString)
<< info.systemLocation()
<< (info.vendorIdentifier() ? QString::number(info.vendorIdentifier(), 16) : blankString)
<< (info.productIdentifier() ? QString::number(info.productIdentifier(), 16) : blankString);
qDebug() << list;
if (not manufacturer.isEmpty())
port = info.systemLocation();
}
if (not port.isEmpty()) {
// Öffnen:
m_serial = new QSerialPort(this);
connect(m_serial, &QSerialPort::readyRead, this, &Server::onReadyReadSensorValueSerial);
// connect(m_serial, &QSerialPort::errorOccurred, this, &Server::startSerial);// d.h. reconnect serial
m_serial->setPortName(port);
m_serial->setBaudRate(115200);
m_serial->setDataBits(QSerialPort::DataBits::Data8);
m_serial->setParity(QSerialPort::Parity::NoParity);
m_serial->setStopBits(QSerialPort::StopBits::OneStop);
m_serial->setFlowControl(QSerialPort::FlowControl::NoFlowControl);
qDebug() << "open" << port << "...";
if (m_serial->open(QIODevice::ReadWrite))
qDebug() << "Serielle Schnittstelle zu" << port;
else
qDebug() << "Keine serielle Schnittstelle";
} else {
qDebug() << "Keine serielle Schnittstelle";
}
SHactuator::setSerial(m_serial);
// SHsensor::displayMem("Server::startSerial ENDE");
}
#endif // OLD
/*
*
*/
void Server::showSensorValues()
{
SHsensor::printALL();
SHsensor::pollTasmota();
// SHsensor::displayMem("Server::showSensorValues ENDE");
}
/*
*
*/
static bool getValueFromJSON(const QJsonObject & jsonInput, const QVector<QString> &P, QString & retValue) {
QJsonObject jsonObj = jsonInput;
for (int i = 0; i<P.size()-1; i++) {
QString p = P[i];
QJsonValue value = jsonObj.value(p);
// qDebug() << value;
jsonObj = value.toObject();
// qDebug() << "jsonObj" << jsonObj;
}
// last:
/* in case of string value get value and convert into string*/
QString last = P[P.size()-1];
// qDebug() << "last: " << jsonObj[last];
QJsonValue value = jsonObj[last];
// qDebug() << "value:" << value.toString();
/* in case of array get array and convert into string*/
// qWarning() << tr("QJsonObject[appName] of value: ") << item["imp"];
// QJsonArray test = item["imp"].toArray();
// qWarning() << test[1].toString();
if (value.isDouble())
retValue = QString::number(value.toDouble());
else if (value.isArray()) {
QJsonArray qja = value.toArray();
QJsonValue v0 = qja[0];
QJsonValue v1 = qja[1];
QJsonValue v2 = qja[2];
if (v0.isDouble() and v1.isDouble() and v2.isDouble()) // solar power
retValue = QString::number(-(v0.toDouble() + v1.toDouble() + v2.toDouble()));
else
retValue = "";
} else
retValue = value.toString();
if (retValue == "")
return false;
return true;
}
/*
*
*/
void Server::processTasmotaMsg(QString jsonMSG)
{
// parse the json message:
QJsonParseError err;
QJsonDocument jsonDoc = QJsonDocument::fromJson(jsonMSG.toUtf8(), &err);
if (err.error != QJsonParseError::NoError) {
qDebug() << "# ERROR Server::tasmotaReply(): wrong JSON format";
return;
}
QJsonObject jsonObj = jsonDoc.object();
QString devName, state, value;
bool isError = true;
if (getValueFromJSON(jsonObj, {"POWER"}, state)) {
// qDebug() << "# ACK message" << state;
isError = false;
} else if (getValueFromJSON(jsonObj, {"Status","DeviceName"}, devName) and
getValueFromJSON(jsonObj, {"StatusSTS", "POWER"}, state) and
getValueFromJSON(jsonObj, {"StatusSNS", "ENERGY", "Power"}, value)) {
// neuer Wert für NOUS:
int nid = SHsensor::nid(devName);
// qDebug() << "# message received for " << devName << "NID" << nid;
if (nid != -1) {
SHsensor::setValue(nid, SID_RELAY, state);
SHsensor::setValue(nid, SID_POWER, value);
// qDebug() << "# message values" << devName << state << value;
isError = false;
}
} else if (getValueFromJSON(jsonObj, {"Status","DeviceName"}, devName) and
getValueFromJSON(jsonObj, {"StatusSNS", "DS18B20", "Temperature"}, value)) {
// neuer Temperatur Wert:
int nid = SHsensor::nid(devName);
// qDebug() << "# message received for " << devName << "NID" << nid;
if (nid != -1) {
SHsensor::setValue(nid, SID_TEMP, value);
// qDebug() << "# message values" << devName << value;
isError = false;
}
}
if (isError)
qDebug() << "# invalid HTTP reply message: ignored";
}
/*
*
*/
void Server::tasmotaReply(QNetworkReply *reply)
{
// SHsensor::displayMem("Server::tasmotaReply");
if (reply->error()) {
qDebug() << reply->errorString();
return;
}
QString jsonMSG = reply->readAll();
reply->deleteLater(); // sonst Memory Leak
processTasmotaMsg(jsonMSG);
// SHsensor::displayMem("Server::tasmotaReply ENDE");
}
/*
*
*/
void Server::onReadyReadSensorValueTCP()
{
// SHsensor::displayMem("Server::onReadyReadSensorValueTCP");
QByteArray data = m_gatewayTCPsocket.readAll();
qDebug() << "TCPMsg=" << data;
SHmessage msg(data);
if (msg.command() == C_SET)
SHsensor::setValue(msg.nodeId(), msg.sensorId(), msg.payload());
// SHsensor::displayMem("Server::onReadyReadSensorValueTCP ENDE");
}
#ifdef OLD
/*
*
*/
void Server::onReadyReadSensorValueSerial()
{
// SHsensor::displayMem("Server::onReadyReadSensorValueSerial");
QByteArray collected;
const QByteArray data = m_serial->readAll();
m_serial->clear();
// qDebug() << "SerialMsg=" << data;
for (auto i : data) {
collected = collected + i;
if (i == '\n') {
// QString qs = QString(collected);
// qDebug() << qs;
SHmessage * msg = new SHmessage(collected);
if (not msg->error() and msg->command() == C_SET)
SHsensor::setValue(msg->nodeId(), msg->sensorId(), msg->payload());
delete msg;
collected.clear();
// SHsensor::displayMem("Server::onReadyReadSensorValueSerial CLEAR");
}
}
// SHsensor::displayMem("Server::onReadyReadSensorValueSerial ENDE");
}
#endif // OLD
/*
*
*/
void Server::onNewClientConnection()
{
mutex.lock();
QWebSocket *pSocket = m_webSocketServer->nextPendingConnection();
if (m_webSocketClients.size() < maxClientNo) {
if (pSocket and pSocket->peerPort() != 0) {
connect(pSocket, &QWebSocket::textMessageReceived, this, &Server::processClientMessage);
// connect(pSocket, &QWebSocket::binaryMessageReceived, this, &ServerInterface::processBinaryMessage);
connect(pSocket, &QWebSocket::disconnected, this, &Server::socketClientDisconnected);
m_webSocketClients.push_back(pSocket);
qDebug() << "New Connection No" << m_webSocketClients.size() << "ID" << pSocket->peerPort();
// showClientsInfo();
} else
qDebug() << "No new Connection (strange) " + QString::number(pSocket->peerPort());
} else
qDebug() << "No new Connection (too many clients) " + QString::number(pSocket->peerPort());
mutex.unlock();
}
/*
*
*/
void Server::processClientMessage(QString message)
{
webSocketMsg wmsg(message);
if (not wmsg.corrupted()) {
if (wmsg.request()) { // fulfill request
QJsonObject jsonObj = wmsg.processGet();
sendToClient(jsonObj);
} else { // set value / mode:
wmsg.processSet();
}
}
}
/*
*
*/
void Server::socketClientDisconnected()
{
mutex.lock();
QWebSocket *pClient = nullptr;
if (m_webSocketClients.size() > 0)
pClient = qobject_cast<QWebSocket *>(sender());
// find disconnected client and erase from vector
for (int i=0; i < m_webSocketClients.size(); i++)
if (m_webSocketClients[i] == pClient) {
m_webSocketClients.erase(m_webSocketClients.begin()+i);
qDebug() << "Delete Connection No" << i << "ID" << pClient->peerPort();
break;
}
// showClientsInfo();
mutex.unlock();
}