Wallbe changed to QModbus

This commit is contained in:
Boernsman 2020-06-16 16:55:45 +02:00
parent 5ffe45baff
commit 78c30f54da
20 changed files with 195 additions and 1262 deletions

View File

@ -6,11 +6,9 @@ QT += \
SOURCES += \
integrationplugindrexelundweiss.cpp \
modbusrtumaster.cpp \
../modbus/modbusrtumaster.cpp \
HEADERS += \
integrationplugindrexelundweiss.h \
modbusrtumaster.h \
modbusdegisterdefinition.h
modbusdegisterdefinition.h \
../modbus/modbusrtumaster.h \

View File

@ -34,8 +34,9 @@
#include "integrations/integrationplugin.h"
#include "plugintimer.h"
#include <QDateTime>
#include "modbusrtumaster.h"
#include <QDateTime>
#include <QSerialPortInfo>
class IntegrationPluginDrexelUndWeiss : public IntegrationPlugin

View File

@ -11,7 +11,8 @@ SOURCES += \
froniusinverter.cpp \
froniusstorage.cpp \
froniusmeter.cpp \
sunspecthing.cpp
sunspecthing.cpp \
../modbus/modbustcpmaster.h \
HEADERS += \
integrationpluginfronius.h \
@ -20,4 +21,5 @@ HEADERS += \
froniusinverter.h \
froniusstorage.h \
froniusmeter.h \
sunspecthing.h
sunspecthing.h \
../modbus/modbustcpmaster.h \

View File

@ -28,7 +28,6 @@
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#include "integrationpluginmodbuscommander.h"
#include "plugininfo.h"
@ -70,20 +69,20 @@ void IntegrationPluginModbusCommander::setupThing(ThingSetupInfo *info)
Thing *thing = info->thing();
if (thing->thingClassId() == modbusTCPClientThingClassId) {
QString ipAddress = thing->paramValue(modbusTCPClientThingIpv4addressParamTypeId).toString();
QHostAddress hostAddress = QHostAddress(thing->paramValue(modbusTCPClientThingIpv4addressParamTypeId).toString());
uint port = thing->paramValue(modbusTCPClientThingPortParamTypeId).toUInt();
foreach (ModbusTCPMaster *modbusTCPMaster, m_modbusTCPMasters.values()) {
if ((modbusTCPMaster->ipv4Address() == ipAddress) && (modbusTCPMaster->port() == port)){
if ((modbusTCPMaster->hostAddress() == hostAddress) && (modbusTCPMaster->port() == port)){
m_modbusTCPMasters.insert(thing, modbusTCPMaster);
return info->finish(Thing::ThingErrorNoError);
}
}
ModbusTCPMaster *modbusTCPMaster = new ModbusTCPMaster(ipAddress, port, this);
ModbusTCPMaster *modbusTCPMaster = new ModbusTCPMaster(hostAddress, port, this);
connect(modbusTCPMaster, &ModbusTCPMaster::connectionStateChanged, this, &IntegrationPluginModbusCommander::onConnectionStateChanged);
connect(modbusTCPMaster, &ModbusTCPMaster::requestExecuted, this, &IntegrationPluginModbusCommander::onRequestExecuted);
connect(modbusTCPMaster, &ModbusTCPMaster::requestError, this, &IntegrationPluginModbusCommander::onRequestError);
connect(modbusTCPMaster, &ModbusTCPMaster::writeRequestExecuted, this, &IntegrationPluginModbusCommander::onRequestExecuted);
connect(modbusTCPMaster, &ModbusTCPMaster::writeRequestError, this, &IntegrationPluginModbusCommander::onRequestError);
connect(modbusTCPMaster, &ModbusTCPMaster::receivedCoil, this, &IntegrationPluginModbusCommander::onReceivedCoil);
connect(modbusTCPMaster, &ModbusTCPMaster::receivedDiscreteInput, this, &IntegrationPluginModbusCommander::onReceivedDiscreteInput);
connect(modbusTCPMaster, &ModbusTCPMaster::receivedHoldingRegister, this, &IntegrationPluginModbusCommander::onReceivedHoldingRegister);

View File

@ -33,8 +33,9 @@
#include "integrations/integrationplugin.h"
#include "plugintimer.h"
#include "modbustcpmaster.h"
#include "modbusrtumaster.h"
#include "../modbus/modbustcpmaster.h"
#include "../modbus/modbusrtumaster.h"
#include <QSerialPortInfo>
#include <QUuid>

View File

@ -1,4 +1,4 @@
include(../plugin.pri)
include(../plugins.pri)
QT += \
serialport \
@ -6,11 +6,10 @@ QT += \
serialbus \
SOURCES += \
integrationpluginmodbuscommander.cpp \
modbustcpmaster.cpp \
modbusrtumaster.cpp \
integrationpluginmodbuscommander.cpp \
../modbus/modbustcpmaster.cpp \
HEADERS += \
integrationpluginmodbuscommander.h \
modbustcpmaster.h \
modbusrtumaster.h \
../modbus/modbustcpmaster.h \

View File

@ -1,371 +0,0 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* Copyright 2013 - 2020, nymea GmbH
* Contact: contact@nymea.io
*
* This file is part of nymea.
* This project including source code and documentation is protected by
* copyright law, and remains the property of nymea GmbH. All rights, including
* reproduction, publication, editing and translation, are reserved. The use of
* this project is subject to the terms of a license agreement to be concluded
* with nymea GmbH in accordance with the terms of use of nymea GmbH, available
* under https://nymea.io/license
*
* GNU Lesser General Public License Usage
* Alternatively, this project may be redistributed and/or modified under the
* terms of the GNU Lesser General Public License as published by the Free
* Software Foundation; version 3. This project is distributed in the hope that
* it will be useful, but WITHOUT ANY WARRANTY; without even the implied
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this project. If not, see <https://www.gnu.org/licenses/>.
*
* For any further details and any questions please contact us under
* contact@nymea.io or see our FAQ/Licensing Information on
* https://nymea.io/license/faq
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#include "modbusrtumaster.h"
#include "extern-plugininfo.h"
#include <QSerialPortInfo>
ModbusRTUMaster::ModbusRTUMaster(QString serialPort, uint baudrate, QSerialPort::Parity parity, uint dataBits, uint stopBits, QObject *parent) :
QObject(parent)
{
m_modbusRtuSerialMaster = new QModbusRtuSerialMaster(this);
m_modbusRtuSerialMaster->setConnectionParameter(QModbusDevice::SerialPortNameParameter, serialPort);
m_modbusRtuSerialMaster->setConnectionParameter(QModbusDevice::SerialBaudRateParameter, baudrate);
m_modbusRtuSerialMaster->setConnectionParameter(QModbusDevice::SerialDataBitsParameter, dataBits);
m_modbusRtuSerialMaster->setConnectionParameter(QModbusDevice::SerialStopBitsParameter, stopBits);
m_modbusRtuSerialMaster->setConnectionParameter(QModbusDevice::SerialParityParameter, parity);
//m_modbusRtuSerialMaster->setTimeout(100);
//m_modbusRtuSerialMaster->setNumberOfRetries(1);
connect(m_modbusRtuSerialMaster, &QModbusTcpClient::stateChanged, this, &ModbusRTUMaster::onModbusStateChanged);
connect(m_modbusRtuSerialMaster, &QModbusRtuSerialMaster::errorOccurred, this, &ModbusRTUMaster::onModbusErrorOccurred);
m_reconnectTimer = new QTimer(this);
m_reconnectTimer->setSingleShot(true);
connect(m_reconnectTimer, &QTimer::timeout, this, &ModbusRTUMaster::onReconnectTimer);
}
ModbusRTUMaster::~ModbusRTUMaster()
{
if (!m_modbusRtuSerialMaster) {
m_modbusRtuSerialMaster->disconnectDevice();
m_modbusRtuSerialMaster->deleteLater();
}
if (!m_reconnectTimer) {
m_reconnectTimer->stop();
m_reconnectTimer->deleteLater();
}
}
bool ModbusRTUMaster::connectDevice()
{
qCDebug(dcModbusCommander()) << "Setting up TCP connecion";
if (!m_modbusRtuSerialMaster)
return false;
return m_modbusRtuSerialMaster->connectDevice();
}
QString ModbusRTUMaster::serialPort()
{
return m_modbusRtuSerialMaster->connectionParameter(QModbusDevice::SerialPortNameParameter).toString();
}
void ModbusRTUMaster::onReconnectTimer()
{
if(!m_modbusRtuSerialMaster->connectDevice()) {
m_reconnectTimer->start(10000);
}
}
QUuid ModbusRTUMaster::readCoil(uint slaveAddress, uint registerAddress)
{
if (!m_modbusRtuSerialMaster) {
return "";
}
QUuid requestId = QUuid::createUuid();
QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::Coils, registerAddress, 1);
if (QModbusReply *reply = m_modbusRtuSerialMaster->sendReadRequest(request, slaveAddress)) {
if (!reply->isFinished()) {
connect(reply, &QModbusReply::finished, this, [reply, requestId, this] {
if (reply->error() == QModbusDevice::NoError) {
requestExecuted(requestId, true);
const QModbusDataUnit unit = reply->result();
uint modbusAddress = unit.startAddress();
emit receivedCoil(reply->serverAddress(), modbusAddress, unit.value(0));
} else {
requestExecuted(requestId, false);
qCWarning(dcModbusCommander()) << "Read response error:" << reply->error();
}
reply->deleteLater();
});
connect(reply, &QModbusReply::errorOccurred, this, [reply, requestId, this] (QModbusDevice::Error error){
qCWarning(dcModbusCommander()) << "Modbus replay error:" << error;
emit requestError(requestId, reply->errorString());
reply->finished(); // To make sure it will be deleted
});
QTimer::singleShot(200, reply, &QModbusReply::deleteLater);
} else {
delete reply; // broadcast replies return immediately
return "";
}
} else {
qCWarning(dcModbusCommander()) << "Read error: " << m_modbusRtuSerialMaster->errorString();
return "";
}
return requestId;
}
QUuid ModbusRTUMaster::writeCoil(uint slaveAddress, uint registerAddress, bool value)
{
if (!m_modbusRtuSerialMaster) {
return "";
}
QUuid requestId = QUuid::createUuid();
QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::Coils, registerAddress, 1);
request.setValue(0, static_cast<uint16_t>(value));
if (QModbusReply *reply = m_modbusRtuSerialMaster->sendWriteRequest(request, slaveAddress)) {
if (!reply->isFinished()) {
connect(reply, &QModbusReply::finished, this, [reply, requestId, this] {
if (reply->error() == QModbusDevice::NoError) {
requestExecuted(requestId, true);
const QModbusDataUnit unit = reply->result();
uint modbusAddress = unit.startAddress();
emit receivedCoil(reply->serverAddress(), modbusAddress, unit.value(0));
} else {
requestExecuted(requestId, false);
qCWarning(dcModbusCommander()) << "Read response error:" << reply->error();
}
reply->deleteLater();
});
connect(reply, &QModbusReply::errorOccurred, this, [reply, requestId, this] (QModbusDevice::Error error){
qCWarning(dcModbusCommander()) << "Modbus replay error:" << error;
emit requestError(requestId, reply->errorString());
reply->finished(); // To make sure it will be deleted
});
QTimer::singleShot(200, reply, &QModbusReply::deleteLater);
} else {
delete reply; // broadcast replies return immediately
return "";
}
} else {
qCWarning(dcModbusCommander()) << "Read error: " << m_modbusRtuSerialMaster->errorString();
return "";
}
return requestId;
}
QUuid ModbusRTUMaster::writeHoldingRegister(uint slaveAddress, uint registerAddress, uint value)
{
if (!m_modbusRtuSerialMaster) {
return "";
}
QUuid requestId = QUuid::createUuid();
QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::HoldingRegisters, registerAddress, 1);
request.setValue(0, static_cast<uint16_t>(value));
if (QModbusReply *reply = m_modbusRtuSerialMaster->sendWriteRequest(request, slaveAddress)) {
if (!reply->isFinished()) {
connect(reply, &QModbusReply::finished, this, [reply, requestId, this] {
if (reply->error() == QModbusDevice::NoError) {
requestExecuted(requestId, true);
const QModbusDataUnit unit = reply->result();
uint modbusAddress = unit.startAddress();
emit receivedHoldingRegister(reply->serverAddress(), modbusAddress, unit.value(0));
} else {
requestExecuted(requestId, false);
qCWarning(dcModbusCommander()) << "Read response error:" << reply->error();
}
reply->deleteLater();
});
connect(reply, &QModbusReply::errorOccurred, this, [reply, requestId, this] (QModbusDevice::Error error){
qCWarning(dcModbusCommander()) << "Modbus replay error:" << error;
emit requestError(requestId, reply->errorString());
reply->finished(); // To make sure it will be deleted
});
QTimer::singleShot(200, reply, &QModbusReply::deleteLater);
} else {
delete reply; // broadcast replies return immediately
return "";
}
} else {
qCWarning(dcModbusCommander()) << "Read error: " << m_modbusRtuSerialMaster->errorString();
return "";
}
return requestId;
}
QUuid ModbusRTUMaster::readDiscreteInput(uint slaveAddress, uint registerAddress)
{
if (!m_modbusRtuSerialMaster) {
return "";
}
QUuid requestId = QUuid::createUuid();
QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::DiscreteInputs, registerAddress, 1);
if (QModbusReply *reply = m_modbusRtuSerialMaster->sendReadRequest(request, slaveAddress)) {
if (!reply->isFinished()) {
connect(reply, &QModbusReply::finished, this, [reply, requestId, this] {
if (reply->error() == QModbusDevice::NoError) {
requestExecuted(requestId, true);
const QModbusDataUnit unit = reply->result();
uint modbusAddress = unit.startAddress();
emit receivedDiscreteInput(reply->serverAddress(), modbusAddress, unit.value(0));
} else {
requestExecuted(requestId, false);
qCWarning(dcModbusCommander()) << "Read response error:" << reply->error();
}
reply->deleteLater();
});
connect(reply, &QModbusReply::errorOccurred, this, [reply, requestId, this] (QModbusDevice::Error error){
qCWarning(dcModbusCommander()) << "Modbus replay error:" << error;
emit requestError(requestId, reply->errorString());
reply->finished(); // To make sure it will be deleted
});
QTimer::singleShot(200, reply, &QModbusReply::deleteLater);
} else {
delete reply; // broadcast replies return immediately
return "";
}
} else {
qCWarning(dcModbusCommander()) << "Read error: " << m_modbusRtuSerialMaster->errorString();
return "";
}
return requestId;
}
QUuid ModbusRTUMaster::readInputRegister(uint slaveAddress, uint registerAddress)
{
if (!m_modbusRtuSerialMaster) {
return "";
}
QUuid requestId = QUuid::createUuid();
QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::InputRegisters, registerAddress, 1);
if (QModbusReply *reply = m_modbusRtuSerialMaster->sendReadRequest(request, slaveAddress)) {
if (!reply->isFinished()) {
connect(reply, &QModbusReply::finished, this, [reply, requestId, this] {
if (reply->error() == QModbusDevice::NoError) {
requestExecuted(requestId, true);
const QModbusDataUnit unit = reply->result();
uint modbusAddress = unit.startAddress();
emit receivedInputRegister(reply->serverAddress(), modbusAddress, unit.value(0));
} else {
requestExecuted(requestId, false);
qCWarning(dcModbusCommander()) << "Read response error:" << reply->error();
}
reply->deleteLater();
});
connect(reply, &QModbusReply::errorOccurred, this, [reply, requestId, this] (QModbusDevice::Error error){
qCWarning(dcModbusCommander()) << "Modbus replay error:" << error;
emit requestError(requestId, reply->errorString());
reply->finished(); // To make sure it will be deleted
});
QTimer::singleShot(200, reply, &QModbusReply::deleteLater);
} else {
delete reply; // broadcast replies return immediately
return "";
}
} else {
qCWarning(dcModbusCommander()) << "Read error: " << m_modbusRtuSerialMaster->errorString();
return "";
}
return requestId;
}
QUuid ModbusRTUMaster::readHoldingRegister(uint slaveAddress, uint registerAddress)
{
if (!m_modbusRtuSerialMaster) {
return "";
}
QUuid requestId = QUuid::createUuid();
QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::HoldingRegisters, registerAddress, 1);
if (QModbusReply *reply = m_modbusRtuSerialMaster->sendReadRequest(request, slaveAddress)) {
if (!reply->isFinished()) {
connect(reply, &QModbusReply::finished, this, [reply, requestId, this] {
if (reply->error() == QModbusDevice::NoError) {
requestExecuted(requestId, true);
const QModbusDataUnit unit = reply->result();
uint modbusAddress = unit.startAddress();
emit receivedHoldingRegister(reply->serverAddress(), modbusAddress, unit.value(0));
} else {
requestExecuted(requestId, false);
qCWarning(dcModbusCommander()) << "Read response error:" << reply->error();
}
reply->deleteLater();
});
connect(reply, &QModbusReply::errorOccurred, this, [reply, requestId, this] (QModbusDevice::Error error){
qCWarning(dcModbusCommander()) << "Modbus replay error:" << error;
emit requestError(requestId, reply->errorString());
reply->finished(); // To make sure it will be deleted
});
QTimer::singleShot(200, reply, &QModbusReply::deleteLater);
} else {
delete reply; // broadcast replies return immediately
return "";
}
} else {
qCWarning(dcModbusCommander()) << "Read error: " << m_modbusRtuSerialMaster->errorString();
return "";
}
return requestId;
}
void ModbusRTUMaster::onModbusErrorOccurred(QModbusDevice::Error error)
{
qCWarning(dcModbusCommander()) << "An error occured" << error;
}
void ModbusRTUMaster::onModbusStateChanged(QModbusDevice::State state)
{
bool connected = (state != QModbusDevice::UnconnectedState);
if (!connected) {
//try to reconnect in 10 seconds
m_reconnectTimer->start(10000);
}
emit connectionStateChanged(connected);
}

View File

@ -1,81 +0,0 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* Copyright 2013 - 2020, nymea GmbH
* Contact: contact@nymea.io
*
* This file is part of nymea.
* This project including source code and documentation is protected by
* copyright law, and remains the property of nymea GmbH. All rights, including
* reproduction, publication, editing and translation, are reserved. The use of
* this project is subject to the terms of a license agreement to be concluded
* with nymea GmbH in accordance with the terms of use of nymea GmbH, available
* under https://nymea.io/license
*
* GNU Lesser General Public License Usage
* Alternatively, this project may be redistributed and/or modified under the
* terms of the GNU Lesser General Public License as published by the Free
* Software Foundation; version 3. This project is distributed in the hope that
* it will be useful, but WITHOUT ANY WARRANTY; without even the implied
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this project. If not, see <https://www.gnu.org/licenses/>.
*
* For any further details and any questions please contact us under
* contact@nymea.io or see our FAQ/Licensing Information on
* https://nymea.io/license/faq
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#ifndef MODBUSRTUMASTER_H
#define MODBUSRTUMASTER_H
#include <QObject>
#include <QtSerialBus>
#include <QSerialPort>
#include <QTimer>
#include <QUuid>
class ModbusRTUMaster : public QObject
{
Q_OBJECT
public:
explicit ModbusRTUMaster(QString serialPort, uint baudrate, QSerialPort::Parity parity, uint dataBits, uint stopBits, QObject *parent = nullptr);
~ModbusRTUMaster();
bool connectDevice();
QUuid readCoil(uint slaveAddress, uint registerAddress);
QUuid readDiscreteInput(uint slaveAddress, uint registerAddress);
QUuid readInputRegister(uint slaveAddress, uint registerAddress);
QUuid readHoldingRegister(uint slaveAddress, uint registerAddress);
QUuid writeCoil(uint slaveAddress, uint registerAddress, bool status);
QUuid writeHoldingRegister(uint slaveAddress, uint registerAddress, uint data);
QString serialPort();
private:
QModbusRtuSerialMaster *m_modbusRtuSerialMaster;
QTimer *m_reconnectTimer = nullptr;
private slots:
void onReconnectTimer();
void onModbusErrorOccurred(QModbusDevice::Error error);
void onModbusStateChanged(QModbusDevice::State state);
signals:
void connectionStateChanged(bool status);
void requestExecuted(QUuid requestId, bool success);
void requestError(QUuid requestId, const QString &error);
void receivedCoil(uint slaveAddress, uint modbusRegister, bool value);
void receivedDiscreteInput(uint slaveAddress, uint modbusRegister, bool value);
void receivedHoldingRegister(uint slaveAddress, uint modbusRegister, uint value);
void receivedInputRegister(uint slaveAddress, uint modbusRegister, uint value);
};
#endif // MODBUSRTUMASTER_H

View File

@ -1,375 +0,0 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* Copyright 2013 - 2020, nymea GmbH
* Contact: contact@nymea.io
*
* This file is part of nymea.
* This project including source code and documentation is protected by
* copyright law, and remains the property of nymea GmbH. All rights, including
* reproduction, publication, editing and translation, are reserved. The use of
* this project is subject to the terms of a license agreement to be concluded
* with nymea GmbH in accordance with the terms of use of nymea GmbH, available
* under https://nymea.io/license
*
* GNU Lesser General Public License Usage
* Alternatively, this project may be redistributed and/or modified under the
* terms of the GNU Lesser General Public License as published by the Free
* Software Foundation; version 3. This project is distributed in the hope that
* it will be useful, but WITHOUT ANY WARRANTY; without even the implied
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this project. If not, see <https://www.gnu.org/licenses/>.
*
* For any further details and any questions please contact us under
* contact@nymea.io or see our FAQ/Licensing Information on
* https://nymea.io/license/faq
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#include "modbustcpmaster.h"
#include "extern-plugininfo.h"
ModbusTCPMaster::ModbusTCPMaster(QString IPv4Address, uint port, QObject *parent) :
QObject(parent)
{
m_modbusTcpClient = new QModbusTcpClient(this);
m_modbusTcpClient->setConnectionParameter(QModbusDevice::NetworkPortParameter, port);
m_modbusTcpClient->setConnectionParameter(QModbusDevice::NetworkAddressParameter, IPv4Address);
//m_modbusTcpClient->setTimeout(100);
//m_modbusTcpClient->setNumberOfRetries(1);
connect(m_modbusTcpClient, &QModbusTcpClient::stateChanged, this, &ModbusTCPMaster::onModbusStateChanged);
connect(m_modbusTcpClient, &QModbusRtuSerialMaster::errorOccurred, this, &ModbusTCPMaster::onModbusErrorOccurred);
m_reconnectTimer = new QTimer(this);
m_reconnectTimer->setSingleShot(true);
connect(m_reconnectTimer, &QTimer::timeout, this, &ModbusTCPMaster::onReconnectTimer);
}
ModbusTCPMaster::~ModbusTCPMaster()
{
if (!m_modbusTcpClient) {
m_modbusTcpClient->disconnectDevice();
m_modbusTcpClient->deleteLater();
}
if (!m_reconnectTimer) {
m_reconnectTimer->stop();
m_reconnectTimer->deleteLater();
}
}
bool ModbusTCPMaster::connectDevice() {
// TCP connction to target device
qCDebug(dcModbusCommander()) << "Setting up TCP connecion";
if (!m_modbusTcpClient)
return false;
return m_modbusTcpClient->connectDevice();
}
uint ModbusTCPMaster::port()
{
return m_modbusTcpClient->connectionParameter(QModbusDevice::NetworkPortParameter).toUInt();
}
bool ModbusTCPMaster::setIPv4Address(QString ipv4Address)
{
m_modbusTcpClient->setConnectionParameter(QModbusDevice::NetworkAddressParameter, ipv4Address);
return connectDevice();
}
bool ModbusTCPMaster::setPort(uint port)
{
m_modbusTcpClient->setConnectionParameter(QModbusDevice::NetworkPortParameter, port);
return connectDevice();
}
void ModbusTCPMaster::onReconnectTimer()
{
if(!m_modbusTcpClient->connectDevice()) {
m_reconnectTimer->start(10000);
}
}
QString ModbusTCPMaster::ipv4Address()
{
return m_modbusTcpClient->connectionParameter(QModbusDevice::NetworkAddressParameter).toString();
}
QUuid ModbusTCPMaster::readCoil(uint slaveAddress, uint registerAddress)
{
if (!m_modbusTcpClient) {
return "";
}
QUuid requestId = QUuid::createUuid();
QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::Coils, registerAddress, 1);
if (QModbusReply *reply = m_modbusTcpClient->sendReadRequest(request, slaveAddress)) {
if (!reply->isFinished()) {
connect(reply, &QModbusReply::finished, this, [reply, requestId, this] {
reply->deleteLater();
if (reply->error() == QModbusDevice::NoError) {
requestExecuted(requestId, true);
const QModbusDataUnit unit = reply->result();
uint modbusAddress = unit.startAddress();
emit receivedCoil(reply->serverAddress(), modbusAddress, unit.value(0));
} else {
requestExecuted(requestId, false);
qCWarning(dcModbusCommander()) << "Read response error:" << reply->error();
}
});
connect(reply, &QModbusReply::errorOccurred, this, [reply, requestId, this] (QModbusDevice::Error error){
qCWarning(dcModbusCommander()) << "Modbus reply error:" << error;
emit requestError(requestId, reply->errorString());
reply->finished(); // To make sure it will be deleted
});
QTimer::singleShot(200, reply, &QModbusReply::deleteLater);
} else {
delete reply; // broadcast replies return immediately
return "";
}
} else {
qCWarning(dcModbusCommander()) << "Read error: " << m_modbusTcpClient->errorString();
return "";
}
return requestId;
}
QUuid ModbusTCPMaster::writeHoldingRegister(uint slaveAddress, uint registerAddress, uint value)
{
if (!m_modbusTcpClient) {
return "";
}
QUuid requestId = QUuid::createUuid();
QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::HoldingRegisters, registerAddress, 1);
request.setValue(0, static_cast<uint16_t>(value));
if (QModbusReply *reply = m_modbusTcpClient->sendWriteRequest(request, slaveAddress)) {
if (!reply->isFinished()) {
connect(reply, &QModbusReply::finished, this, [reply, requestId, this] {
if (reply->error() == QModbusDevice::NoError) {
requestExecuted(requestId, true);
const QModbusDataUnit unit = reply->result();
uint modbusAddress = unit.startAddress();
emit receivedHoldingRegister(reply->serverAddress(), modbusAddress, unit.value(0));
} else {
requestExecuted(requestId, false);
qCWarning(dcModbusCommander()) << "Read response error:" << reply->error();
}
reply->deleteLater();
});
connect(reply, &QModbusReply::errorOccurred, this, [reply, requestId, this] (QModbusDevice::Error error){
qCWarning(dcModbusCommander()) << "Modbus replay error:" << error;
emit requestError(requestId, reply->errorString());
reply->finished(); // To make sure it will be deleted
});
QTimer::singleShot(200, reply, &QModbusReply::deleteLater);
} else {
delete reply; // broadcast replies return immediately
return "";
}
} else {
qCWarning(dcModbusCommander()) << "Read error: " << m_modbusTcpClient->errorString();
return "";
}
return requestId;
}
QUuid ModbusTCPMaster::readDiscreteInput(uint slaveAddress, uint registerAddress)
{
if (!m_modbusTcpClient) {
return "";
}
QUuid requestId = QUuid::createUuid();
QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::DiscreteInputs, registerAddress, 1);
if (QModbusReply *reply = m_modbusTcpClient->sendReadRequest(request, slaveAddress)) {
if (!reply->isFinished()) {
connect(reply, &QModbusReply::finished, this, [reply, requestId, this] {
reply->deleteLater();
if (reply->error() == QModbusDevice::NoError) {
requestExecuted(requestId, true);
const QModbusDataUnit unit = reply->result();
uint modbusAddress = unit.startAddress();
emit receivedDiscreteInput(reply->serverAddress(), modbusAddress, unit.value(0));
} else {
requestExecuted(requestId, false);
qCWarning(dcModbusCommander()) << "Read response error:" << reply->error();
}
});
connect(reply, &QModbusReply::errorOccurred, this, [requestId, this] (QModbusDevice::Error error){
qCWarning(dcModbusCommander()) << "Modbus replay error:" << error;
QModbusReply *reply = qobject_cast<QModbusReply *>(sender());
emit requestError(requestId, reply->errorString());
reply->finished(); // To make sure it will be deleted
});
QTimer::singleShot(200, reply, &QModbusReply::deleteLater);
} else {
delete reply; // broadcast replies return immediately
return "";
}
} else {
qCWarning(dcModbusCommander()) << "Read error: " << m_modbusTcpClient->errorString();
return "";
}
return requestId;
}
QUuid ModbusTCPMaster::readInputRegister(uint slaveAddress, uint registerAddress)
{
if (!m_modbusTcpClient) {
return "";
}
QUuid requestId = QUuid::createUuid();
QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::InputRegisters, registerAddress, 1);
if (QModbusReply *reply = m_modbusTcpClient->sendReadRequest(request, slaveAddress)) {
if (!reply->isFinished()) {
connect(reply, &QModbusReply::finished, this, [reply, requestId, this] {
reply->deleteLater();
if (reply->error() == QModbusDevice::NoError) {
requestExecuted(requestId, true);
const QModbusDataUnit unit = reply->result();
uint modbusAddress = unit.startAddress();
emit receivedInputRegister(reply->serverAddress(), modbusAddress, unit.value(0));
} else {
requestExecuted(requestId, false);
qCWarning(dcModbusCommander()) << "Read response error:" << reply->error();
}
});
connect(reply, &QModbusReply::errorOccurred, this, [reply, requestId, this] (QModbusDevice::Error error){
qCWarning(dcModbusCommander()) << "Modbus reply error:" << error;
emit requestError(requestId, reply->errorString());
reply->finished(); // To make sure it will be deleted
});
QTimer::singleShot(200, reply, &QModbusReply::deleteLater);
} else {
delete reply; // broadcast replies return immediately
return "";
}
} else {
qCWarning(dcModbusCommander()) << "Read error: " << m_modbusTcpClient->errorString();
return "";
}
return requestId;
}
QUuid ModbusTCPMaster::readHoldingRegister(uint slaveAddress, uint registerAddress)
{
if (!m_modbusTcpClient) {
return "";
}
QUuid requestId = QUuid::createUuid();
QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::HoldingRegisters, registerAddress, 1);
if (QModbusReply *reply = m_modbusTcpClient->sendReadRequest(request, slaveAddress)) {
if (!reply->isFinished()) {
connect(reply, &QModbusReply::finished, this, [reply, requestId, this] {
if (reply->error() == QModbusDevice::NoError) {
requestExecuted(requestId, true);
const QModbusDataUnit unit = reply->result();
uint modbusAddress = unit.startAddress();
emit receivedHoldingRegister(reply->serverAddress(), modbusAddress, unit.value(0));
} else {
requestExecuted(requestId, false);
qCWarning(dcModbusCommander()) << "Read response error:" << reply->error();
}
reply->deleteLater();
});
connect(reply, &QModbusReply::errorOccurred, this, [reply, requestId, this] (QModbusDevice::Error error){
qCWarning(dcModbusCommander()) << "Modbus replay error:" << error;
emit requestError(requestId, reply->errorString());
reply->finished(); // To make sure it will be deleted
});
QTimer::singleShot(200, reply, &QModbusReply::deleteLater);
} else {
delete reply; // broadcast replies return immediately
return "";
}
} else {
qCWarning(dcModbusCommander()) << "Read error: " << m_modbusTcpClient->errorString();
return "";
}
return requestId;
}
QUuid ModbusTCPMaster::writeCoil(uint slaveAddress, uint registerAddress, bool value)
{
if (!m_modbusTcpClient) {
return "";
}
QUuid requestId = QUuid::createUuid();
QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::Coils, registerAddress, 1);
request.setValue(0, static_cast<uint16_t>(value));
if (QModbusReply *reply = m_modbusTcpClient->sendWriteRequest(request, slaveAddress)) {
if (!reply->isFinished()) {
connect(reply, &QModbusReply::finished, this, [reply, requestId, this] () {
if (reply->error() == QModbusDevice::NoError) {
requestExecuted(requestId, true);
const QModbusDataUnit unit = reply->result();
uint modbusAddress = unit.startAddress();
emit receivedCoil(reply->serverAddress(), modbusAddress, unit.value(0));
} else {
requestExecuted(requestId, false);
qCWarning(dcModbusCommander()) << "Read response error:" << reply->error();
}
reply->deleteLater();
});
connect(reply, &QModbusReply::errorOccurred, this, [reply, requestId, this] (QModbusDevice::Error error){
qCWarning(dcModbusCommander()) << "Modbus reply error:" << error;
emit requestError(requestId, reply->errorString());
reply->finished(); // To make sure it will be deleted
});
QTimer::singleShot(200, reply, &QModbusReply::deleteLater);
} else {
delete reply; // broadcast replies return immediately
return "";
}
} else {
qCWarning(dcModbusCommander()) << "Read error: " << m_modbusTcpClient->errorString();
return "";
}
return requestId;
}
void ModbusTCPMaster::onModbusErrorOccurred(QModbusDevice::Error error)
{
qCWarning(dcModbusCommander()) << "An error occured" << error;
}
void ModbusTCPMaster::onModbusStateChanged(QModbusDevice::State state)
{
bool connected = (state != QModbusDevice::UnconnectedState);
if (!connected) {
//try to reconnect in 10 seconds
m_reconnectTimer->start(10000);
}
emit connectionStateChanged(connected);
}

View File

@ -1,85 +0,0 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* Copyright 2013 - 2020, nymea GmbH
* Contact: contact@nymea.io
*
* This file is part of nymea.
* This project including source code and documentation is protected by
* copyright law, and remains the property of nymea GmbH. All rights, including
* reproduction, publication, editing and translation, are reserved. The use of
* this project is subject to the terms of a license agreement to be concluded
* with nymea GmbH in accordance with the terms of use of nymea GmbH, available
* under https://nymea.io/license
*
* GNU Lesser General Public License Usage
* Alternatively, this project may be redistributed and/or modified under the
* terms of the GNU Lesser General Public License as published by the Free
* Software Foundation; version 3. This project is distributed in the hope that
* it will be useful, but WITHOUT ANY WARRANTY; without even the implied
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this project. If not, see <https://www.gnu.org/licenses/>.
*
* For any further details and any questions please contact us under
* contact@nymea.io or see our FAQ/Licensing Information on
* https://nymea.io/license/faq
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#ifndef MODBUSTCPMASTER_H
#define MODBUSTCPMASTER_H
#include <QObject>
#include <QHostAddress>
#include <QtSerialBus>
#include <QTimer>
#include <QUuid>
class ModbusTCPMaster : public QObject
{
Q_OBJECT
public:
explicit ModbusTCPMaster(QString ipAddress, uint port, QObject *parent = nullptr);
~ModbusTCPMaster();
bool connectDevice();
QUuid readCoil(uint slaveAddress, uint registerAddress);
QUuid readDiscreteInput(uint slaveAddress, uint registerAddress);
QUuid readInputRegister(uint slaveAddress, uint registerAddress);
QUuid readHoldingRegister(uint slaveAddress, uint registerAddress);
QUuid writeCoil(uint slaveAddress, uint registerAddress, bool status);
QUuid writeHoldingRegister(uint slaveAddress, uint registerAddress, uint data);
QString ipv4Address();
uint port();
bool setIPv4Address(QString ipAddress);
bool setPort(uint port);
private:
QTimer *m_reconnectTimer = nullptr;
QModbusTcpClient *m_modbusTcpClient;
private slots:
void onReconnectTimer();
void onModbusErrorOccurred(QModbusDevice::Error error);
void onModbusStateChanged(QModbusDevice::State state);
signals:
void connectionStateChanged(bool status);
void requestExecuted(QUuid requestId, bool success);
void requestError(QUuid requestId, const QString &error);
void receivedCoil(uint slaveAddress, uint modbusRegister, bool value);
void receivedDiscreteInput(uint slaveAddress, uint modbusRegister, bool value);
void receivedHoldingRegister(uint slaveAddress, uint modbusRegister, uint value);
void receivedInputRegister(uint slaveAddress, uint modbusRegister, uint value);
};
#endif // MODBUSTCPMASTER_H

View File

@ -36,7 +36,6 @@
IntegrationPluginMyPv::IntegrationPluginMyPv()
{
}
@ -162,14 +161,14 @@ void IntegrationPluginMyPv::executeAction(ThingActionInfo *info)
if (action.actionTypeId() == elwaHeatingPowerActionTypeId) {
int heatingPower = action.param(elwaHeatingPowerActionHeatingPowerParamTypeId).value().toInt();
if(!modbusTCPMaster->setRegister(0xff, ElwaModbusRegisters::Power, heatingPower)){
if(!modbusTCPMaster->writeHoldingRegister(0xff, ElwaModbusRegisters::Power, heatingPower)){
return info->finish(Thing::ThingErrorHardwareFailure);
}
return;
} else if (action.actionTypeId() == elwaPowerActionTypeId) {
bool power = action.param(elwaHeatingPowerActionHeatingPowerParamTypeId).value().toBool();
if(power) {
if(!modbusTCPMaster->setRegister(0xff, ElwaModbusRegisters::ManuelStart, 1)){
if(!modbusTCPMaster->writeHoldingRegister(0xff, ElwaModbusRegisters::ManuelStart, 1)){
return info->finish(Thing::ThingErrorHardwareFailure);
}
}
@ -188,13 +187,18 @@ void IntegrationPluginMyPv::onRefreshTimer(){
}
}
void IntegrationPluginMyPv::onConnectionStateChanged(bool status)
{
//TODO set device connected state
}
void IntegrationPluginMyPv::update(Thing *thing) {
if (thing->thingClassId() == elwaThingClassId)
{
ModbusTCPMaster *modbusTCPMaster = m_modbusTcpMasters.value(thing);
int data;
if (modbusTCPMaster->getRegister(0xff, ElwaModbusRegisters::Status, &data)) {
if (modbusTCPMaster->readHoldingRegister(0xff, ElwaModbusRegisters::Status, &data)) {
switch (data) {
case Heating: {
thing->setStateValue(elwaStatusStateTypeId, "Heating");

View File

@ -33,7 +33,8 @@
#include "integrations/integrationplugin.h"
#include "plugintimer.h"
#include "modbustcpmaster.h"
#include "../modbus/modbustcpmaster.h"
#include <QUdpSocket>
@ -84,6 +85,16 @@ private:
private slots:
void onRefreshTimer();
void onPluginConfigurationChanged(const ParamTypeId &paramTypeId, const QVariant &value);
void onConnectionStateChanged(bool status);
void onRequestExecuted(QUuid requestId, bool success);
void onRequestError(QUuid requestId, const QString &error);
void onReceivedCoil(quint32 slaveAddress, quint32 modbusRegister, bool value);
void onReceivedDiscreteInput(quint32 slaveAddress, quint32 modbusRegister, bool value);
void onReceivedHoldingRegister(quint32 slaveAddress, quint32 modbusRegister, int value);
void onReceivedInputRegister(quint32 slaveAddress, quint32 modbusRegister, int value);
};
#endif // INTEGRATIONPLUGINMYPV_H

View File

@ -1,184 +0,0 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* Copyright 2013 - 2020, nymea GmbH
* Contact: contact@nymea.io
*
* This file is part of nymea.
* This project including source code and documentation is protected by
* copyright law, and remains the property of nymea GmbH. All rights, including
* reproduction, publication, editing and translation, are reserved. The use of
* this project is subject to the terms of a license agreement to be concluded
* with nymea GmbH in accordance with the terms of use of nymea GmbH, available
* under https://nymea.io/license
*
* GNU Lesser General Public License Usage
* Alternatively, this project may be redistributed and/or modified under the
* terms of the GNU Lesser General Public License as published by the Free
* Software Foundation; version 3. This project is distributed in the hope that
* it will be useful, but WITHOUT ANY WARRANTY; without even the implied
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this project. If not, see <https://www.gnu.org/licenses/>.
*
* For any further details and any questions please contact us under
* contact@nymea.io or see our FAQ/Licensing Information on
* https://nymea.io/license/faq
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#include "modbustcpmaster.h"
#include "extern-plugininfo.h"
#include <QLoggingCategory>
Q_DECLARE_LOGGING_CATEGORY(dcModbus)
Q_LOGGING_CATEGORY(dcModbus, "Modbus")
ModbusTCPMaster::ModbusTCPMaster(QHostAddress IPv4Address, int port, QObject *parent) :
QObject(parent),
m_IPv4Address(IPv4Address),
m_port(port)
{
}
ModbusTCPMaster::~ModbusTCPMaster()
{
if (m_mb != NULL) {
modbus_close(m_mb);
}
modbus_free(m_mb);
}
bool ModbusTCPMaster::createInterface() {
// TCP connction to target device
qCDebug(dcModbus()) << "Setting up TCP connecion" << m_IPv4Address.toString() << "port:" << m_port;
const char *address = m_IPv4Address.toString().toLatin1().data();
m_mb = modbus_new_tcp(address, m_port);
if(m_mb == nullptr){
qCWarning(dcMypv()) << "Error modbus TCP: " << modbus_strerror(errno) ;
return false;
}
// Extend the timeout to 3 seconds
//struct timeval response_timeout;
//response_timeout.tv_sec = 3;
//response_timeout.tv_usec = 0;
//modbus_set_response_timeout(m_mb, &response_timeout);
if(modbus_connect(m_mb) == -1){
qCWarning(dcMypv()) << "Error connecting modbus:" << modbus_strerror(errno) ;
return false;
}
return true;
}
int ModbusTCPMaster::port()
{
return m_port;
}
bool ModbusTCPMaster::setIPv4Address(QHostAddress ipv4Address)
{
m_IPv4Address = ipv4Address;
if (!createInterface()) {
return false;
}
return true;
}
bool ModbusTCPMaster::setPort(int port)
{
m_port = port;
if (!createInterface()) {
return false;
}
return true;
}
QHostAddress ModbusTCPMaster::ipv4Address()
{
return m_IPv4Address;
}
bool ModbusTCPMaster::setCoil(int slaveAddress, int coilAddress, bool status)
{
if (m_mb == nullptr) {
if (!createInterface())
return false;
}
if(modbus_set_slave(m_mb, slaveAddress) == -1){
qCWarning(dcMypv()) << "Error setting slave ID" << slaveAddress << "Reason:" << modbus_strerror(errno) ;
return false;
}
if (modbus_write_bit(m_mb, coilAddress, status) == -1) {
qCWarning(dcMypv()) << "Could not write Coil" << coilAddress << "Reason:" << modbus_strerror(errno);
return false;
}
return true;
}
bool ModbusTCPMaster::setRegister(int slaveAddress, int registerAddress, int data)
{
if (m_mb == nullptr) {
if (!createInterface())
return false;
}
if(modbus_set_slave(m_mb, slaveAddress) == -1){
qCWarning(dcMypv()) << "Error setting slave ID" << slaveAddress << "Reason:" << modbus_strerror(errno) ;
return false;
}
if (modbus_write_register(m_mb, registerAddress, data) == -1) {
qCWarning(dcMypv()) << "Could not write Register" << registerAddress << "Reason:" << modbus_strerror(errno);
return false;
}
return true;
}
bool ModbusTCPMaster::getCoil(int slaveAddress, int coilAddress, bool *result)
{
if (m_mb == nullptr) {
if (!createInterface())
return false;
}
if(modbus_set_slave(m_mb, slaveAddress) == -1){
qCWarning(dcMypv()) << "Error setting slave ID" << slaveAddress << "Reason:" << modbus_strerror(errno) ;
return false;
}
uint8_t status;
if (modbus_read_bits(m_mb, coilAddress, 1, &status) == -1){
qCWarning(dcMypv()) << "Could not read bits" << coilAddress << "Reason:"<< modbus_strerror(errno);
return false;
}
*result = (bool)status;
return true;
}
bool ModbusTCPMaster::getRegister(int slaveAddress, int registerAddress, int *result)
{
uint16_t data;
if (m_mb == nullptr) {
if (!createInterface())
return false;
}
if(modbus_set_slave(m_mb, slaveAddress) == -1){
qCWarning(dcMypv()) << "Error setting slave ID" << slaveAddress << "Reason:" << modbus_strerror(errno) ;
return false;
}
if (modbus_read_registers(m_mb, registerAddress, 1, &data) == -1){
qCWarning(dcMypv()) << "Could not read register" << registerAddress << "Reason:" << modbus_strerror(errno);
return false;
}
*result = data;
return true;
}

View File

@ -1,66 +0,0 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* Copyright 2013 - 2020, nymea GmbH
* Contact: contact@nymea.io
*
* This file is part of nymea.
* This project including source code and documentation is protected by
* copyright law, and remains the property of nymea GmbH. All rights, including
* reproduction, publication, editing and translation, are reserved. The use of
* this project is subject to the terms of a license agreement to be concluded
* with nymea GmbH in accordance with the terms of use of nymea GmbH, available
* under https://nymea.io/license
*
* GNU Lesser General Public License Usage
* Alternatively, this project may be redistributed and/or modified under the
* terms of the GNU Lesser General Public License as published by the Free
* Software Foundation; version 3. This project is distributed in the hope that
* it will be useful, but WITHOUT ANY WARRANTY; without even the implied
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this project. If not, see <https://www.gnu.org/licenses/>.
*
* For any further details and any questions please contact us under
* contact@nymea.io or see our FAQ/Licensing Information on
* https://nymea.io/license/faq
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#ifndef MODBUSTCPMASTER_H
#define MODBUSTCPMASTER_H
#include <QObject>
#include <QHostAddress>
#include <modbus/modbus.h>
class ModbusTCPMaster : public QObject
{
Q_OBJECT
public:
explicit ModbusTCPMaster(QHostAddress IPv4Address, int port, QObject *parent = 0);
~ModbusTCPMaster();
bool createInterface();
bool getCoil(int slaveAddress, int coilAddress, bool *result);
bool getRegister(int slaveAddress, int registerAddress, int *result);
bool setCoil(int slaveAddress, int coilAddress, bool status);
bool setRegister(int slaveAddress, int registerAddress, int data);
QHostAddress ipv4Address();
int port();
bool setIPv4Address(QHostAddress IPv4Address);
bool setPort(int port);
private:
modbus_t *m_mb;
QHostAddress m_IPv4Address;
int m_port;
signals:
public slots:
};
#endif // MODBUSTCPMASTER_H

View File

@ -6,10 +6,9 @@ QT += \
SOURCES += \
integrationpluginmypv.cpp \
modbustcpmaster.cpp \
../modbus/modbustcpmaster.cpp \
HEADERS += \
integrationpluginmypv.h \
modbustcpmaster.h \
../modbus/modbustcpmaster.h \

View File

@ -52,15 +52,24 @@ void IntegrationPluginWallbe::setupThing(ThingSetupInfo *info)
QHostAddress address(thing->paramValue(wallbeEcoThingIpParamTypeId).toString());
if (address.isNull()){
qCWarning(dcWallbe) << "IP address is null";
qCWarning(dcWallbe) << "IP address is not valid";
info->finish(Thing::ThingErrorSetupFailed, tr("IP address parameter not valid"));
return;
}
ModbusTCPMaster *modbusTcpMaster = new ModbusTCPMaster(address, 502, this);
connect(modbusTcpMaster, &ModbusTCPMaster::connectionStateChanged, this, &IntegrationPluginWallbe::onConnectionStateChanged);
connect(modbusTcpMaster, &ModbusTCPMaster::receivedHoldingRegister, this, &IntegrationPluginWallbe::onReceivedHoldingRegister);
connect(modbusTcpMaster, &ModbusTCPMaster::writeRequestExecuted, this, &IntegrationPluginWallbe::onWriteRequestExecuted);
connect(modbusTcpMaster, &ModbusTCPMaster::writeRequestError, this, &IntegrationPluginWallbe::onWriteRequestError);
WallBe *wallbe = new WallBe(address, 502, this);
m_connections.insert(thing, wallbe);
info->finish(Thing::ThingErrorNoError);
m_connections.insert(thing, modbusTcpMaster);
connect(modbusTcpMaster, &ModbusTCPMaster::connectionStateChanged, info, [this, modbusTcpMaster](bool connected) {
if(connected) {
info->finish(Thing::ThingErrorNoError);
} else {
info->finish(Thing::ThingErrorHardwareNotAvailable);
}
});
}
@ -85,12 +94,11 @@ void IntegrationPluginWallbe::discoverThings(ThingDiscoveryInfo *info)
}
info->finish(Thing::ThingErrorNoError);
});
return;
} else {
Q_ASSERT_X(false, "discoverThings", QString("Unhandled thingClassId: %1").arg(info->thingClassId().toString()).toUtf8());
}
Q_ASSERT_X(false, "discoverThings", QString("Unhandled thingClassId: %1").arg(info->thingClassId().toString()).toUtf8());
}
void IntegrationPluginWallbe::postSetupThing(Thing *thing)
{
if (!m_pluginTimer) {
@ -115,9 +123,9 @@ void IntegrationPluginWallbe::executeAction(ThingActionInfo *info)
Thing *thing = info->thing();
Action action = info->action();
WallBe *wallbe = m_connections.value(thing);
if (!wallbe) {
qCWarning(dcWallbe()) << "Wallbe object not available";
ModbusTCPMaster *modbusTcpMaster = m_connections.value(thing);
if (!modbusTcpMaster) {
qCWarning(dcWallbe()) << "Modbus connection not available";
info->finish(Thing::ThingErrorHardwareFailure);
return;
}
@ -130,34 +138,37 @@ void IntegrationPluginWallbe::executeAction(ThingActionInfo *info)
// get the param value of the charging action
bool charging = action.param(wallbeEcoPowerActionPowerParamTypeId).value().toBool();
qCDebug(dcWallbe) << "start Charging button" << thing->name() << "set power to" << charging;
wallbe->setChargingStatus(charging);
QUuid requestId = modbusTcpMaster->writeCoil(0xff, WallbeRegisterAddress::ChargingStatus, charging);
// Set the "power" state
thing->setStateValue(wallbeEcoPowerStateTypeId, charging);
info->finish(Thing::ThingErrorNoError);
return;
m_asyncActions.insert(requestId, info);
connect(info, &ThingActionInfo::aborted, this, [this, requestId] {m_asyncActions.remove(requestId);});
} else if(action.actionTypeId() == wallbeEcoChargeCurrentActionTypeId){
uint16_t current = action.param(wallbeEcoChargeCurrentEventChargeCurrentParamTypeId).value().toUInt();
qCDebug(dcWallbe) << "Charging power set to" << current;
wallbe->setChargingCurrent(current);
QUuid requestId = modbusTcpMaster->writeCoil(0xff, WallbeRegisterAddress::ChargingCurrent, current);
thing->setStateValue(wallbeEcoChargeCurrentStateTypeId, current);
info->finish(Thing::ThingErrorNoError);
return;
}
Q_ASSERT_X(false, "executeAction", QString("Unhandled action: %1").arg(action.actionTypeId().toString()).toUtf8());
}
Q_ASSERT_X(false, "executeAction", QString("Unhandled thingClassId: %1").arg(thing->thingClassId().toString()).toUtf8());
}
m_asyncActions.insert(requestId, info);
connect(info, &ThingActionInfo::aborted, this, [this, requestId] {m_asyncActions.remove(requestId);});
} else {
Q_ASSERT_X(false, "executeAction", QString("Unhandled action: %1").arg(action.actionTypeId().toString()).toUtf8());
}
} else {
Q_ASSERT_X(false, "executeAction", QString("Unhandled thingClassId: %1").arg(thing->thingClassId().toString()).toUtf8());
}
}
void IntegrationPluginWallbe::thingRemoved(Thing *thing)
{
m_address.removeOne(QHostAddress(thing->paramValue(wallbeEcoThingIpParamTypeId).toString()));
WallBe *wallbe = m_connections.take(thing);
if (wallbe) {
qCDebug(dcWallbe) << "Remove device" << thing->name();
wallbe->deleteLater();
if (thing->thingClassId() == wallbeEcoThingClassId) {
if (m_connections.contains(thing)) {
ModbusTCPMaster *modbusTcpMaster = m_connections.take(thing);
qCDebug(dcWallbe) << "Remove device" << thing->name();
modbusTcpMaster->deleteLater();
}
}
if (myThings().isEmpty()) {
@ -167,43 +178,91 @@ void IntegrationPluginWallbe::thingRemoved(Thing *thing)
}
void IntegrationPluginWallbe::update(Thing *thing)
{
WallBe * wallbe = m_connections.value(thing);
if(!wallbe->isAvailable())
{
ModbusTCPMaster *modbusTCPMaster = m_connections.value(thing);
if(!modbusTCPMaster)
return;
modbusTCPMaster->readHoldingRegister(0xff, WallbeRegisterAddress::EVStatus);
modbusTCPMaster->readHoldingRegister(0xff, WallbeRegisterAddress::ChargingCurrent);
modbusTCPMaster->readHoldingRegister(0xff, WallbeRegisterAddress::ChargingStatus);
modbusTCPMaster->readHoldingRegister(0xff, WallbeRegisterAddress::ChargingTime);
}
void IntegrationPluginWallbe::onConnectionStateChanged(bool status)
{
ModbusTCPMaster *modbusTCPMaster = static_cast<ModbusTCPMaster *>(sender());
Thing *thing = m_connections.key(modbusTCPMaster);
if (!thing)
return;
thing->setStateValue(wallbeEcoConnectedStateTypeId, status);
}
void IntegrationPluginWallbe::onReceivedHoldingRegister(int slaveAddress, int modbusRegister, const QVector<quint16> &value)
{
Q_UNUSED(slaveAddress)
ModbusTCPMaster *modbusTCPMaster = static_cast<ModbusTCPMaster *>(sender());
Thing *thing = m_connections.key(modbusTCPMaster);
if (!thing)
return;
thing->setStateValue(wallbeEcoConnectedStateTypeId, true);
//EV state - 16 bit ASCII (8bit)
switch (wallbe->getEvStatus()) {
case 65:
thing->setStateValue(wallbeEcoEvStatusStateTypeId, "A - No car plugged in");
switch (WallbeRegisterAddress(modbusRegister)) {
case WallbeRegisterAddress::EVStatus:
//EV state - 16 bit ASCII (8bit)
switch (value[0]) {
case 65:
thing->setStateValue(wallbeEcoEvStatusStateTypeId, "A - No car plugged in");
break;
case 66:
thing->setStateValue(wallbeEcoEvStatusStateTypeId, "B - Supply equipment not yet ready");
break;
case 67:
thing->setStateValue(wallbeEcoEvStatusStateTypeId, "C - Ready to charge");
break;
case 68:
thing->setStateValue(wallbeEcoEvStatusStateTypeId, "D - Ready to charge, ventilation needed");
break;
case 69:
thing->setStateValue(wallbeEcoEvStatusStateTypeId, "E - Short circuit detected");
break;
case 70:
thing->setStateValue(wallbeEcoEvStatusStateTypeId, "F - Supply equipment not available");
break;
default:
thing->setStateValue(wallbeEcoEvStatusStateTypeId, "F - Supply equipment not available");
}
break;
case 66:
thing->setStateValue(wallbeEcoEvStatusStateTypeId, "B - Supply equipment not yet ready");
case WallbeRegisterAddress::ChargingStatus:
thing->setStateValue(wallbeEcoPowerStateTypeId, value[0]);
break;
case WallbeRegisterAddress::ChargingTime: {
// Extract Input Register 102 - load time - 32bit integer
int minutes = (((uint32_t)(value[0]<<16)|(uint32_t)(value[1]))/60); //Converts to minutes
thing->setStateValue(wallbeEcoChargeTimeStateTypeId, minutes);
}
break;
case WallbeRegisterAddress::ChargingCurrent:
thing->setStateValue(wallbeEcoChargeCurrentStateTypeId, value[0]);
break;
case WallbeRegisterAddress::ErrorCode:
qCDebug(dcWallbe()) << "Received Error Code modbus register" << value[0];
break;
case 67:
thing->setStateValue(wallbeEcoEvStatusStateTypeId, "C - Ready to charge");
break;
case 68:
thing->setStateValue(wallbeEcoEvStatusStateTypeId, "D - Ready to charge, ventilation needed");
break;
case 69:
thing->setStateValue(wallbeEcoEvStatusStateTypeId, "E - Short circuit detected");
break;
case 70:
thing->setStateValue(wallbeEcoEvStatusStateTypeId, "F - Supply equipment not available");
break;
default:
thing->setStateValue(wallbeEcoEvStatusStateTypeId, "F - Supply equipment not available");
}
qCDebug(dcWallbe) << "EV State:" << thing->stateValue(wallbeEcoEvStatusStateTypeId).toString();
// Extract Input Register 102 - load time - 32bit integer
thing->setStateValue(wallbeEcoChargeTimeStateTypeId, wallbe->getChargingTime());
// Read the charge current state
thing->setStateValue(wallbeEcoChargeCurrentStateTypeId, wallbe->getChargingCurrent());
thing->setStateValue(wallbeEcoPowerStateTypeId, wallbe->getChargingStatus());
}
void IntegrationPluginWallbe::onWriteRequestExecuted(const QUuid &requestId, bool success)
{
if (m_asyncActions.contains(requestId)) {
ThingActionInfo *info = m_asyncActions.value(requestId);
if (success) {
info->finish(Thing::ThingErrorNoError);
} else {
info->finish(Thing::ThingErrorHardwareFailure);
}
}
}
void IntegrationPluginWallbe::onWriteRequestError(const QUuid &requestId, const QString &error)
{
Q_UNUSED(requestId)
qCWarning(dcWallbe()) << "Could not execute write request" << error;
}

View File

@ -32,10 +32,11 @@
#define INTEGRATIONPLUGINWALLBE_H
#include "integrations/integrationplugin.h"
#include "wallbe.h"
#include "plugintimer.h"
#include "host.h"
#include "discover.h"
#include "plugintimer.h"
#include "../modbus/modbustcpmaster.h"
#include <QObject>
#include <QHostAddress>
@ -48,6 +49,14 @@ class IntegrationPluginWallbe : public IntegrationPlugin
Q_INTERFACES(IntegrationPlugin)
public:
enum WallbeRegisterAddress {
EVStatus = 100,
ChargingTime = 102,
ErrorCode = 107,
ChargingCurrent = 300,
ChargingStatus = 400,
};
explicit IntegrationPluginWallbe();
void discoverThings(ThingDiscoveryInfo *info) override;
@ -57,11 +66,23 @@ public:
void thingRemoved(Thing *thing) override;
private:
QHash<Thing *, WallBe *> m_connections;
QList<QHostAddress> m_address;
QHash<Thing *, ModbusTCPMaster *> m_connections;
PluginTimer *m_pluginTimer = nullptr;
QHash<QUuid, ThingActionInfo *> m_asyncActions;
void update(Thing *thing);
private slots:
void onRefreshTimer();
void onConnectionStateChanged(bool status);
void onReceivedCoil(int slaveAddress, int modbusRegister, bool value);
void onReceivedDiscreteInput(int slaveAddress, int modbusRegister, bool value);
void onReceivedHoldingRegister(int slaveAddress, int modbusRegister, const QVector<quint16> &value);
void onReceivedInputRegister(int slaveAddress, int modbusRegister, int value);
void onWriteRequestExecuted(const QUuid &requestId, bool success);
void onWriteRequestError(const QUuid &requestId, const QString &error);
};
#endif // INTEGRATIONPLUGINWALLBE_H

View File

@ -171,7 +171,7 @@ int WallBe::getChargingTime()
if(modbus_read_registers(m_device, 102, 2, reg) == -1)
return 0;
return (((uint32_t)(reg[2]<<16)|(uint32_t)(reg[3]))/60); //Converts to minutes
return
}
void WallBe::setChargingCurrent(int current)

View File

@ -35,14 +35,15 @@
#include <QHostAddress>
#include <QProcess>
#include <modbus/modbus.h>
#include "../modbus/modbustcpmaster.h"
class WallBe : public QObject
{
Q_OBJECT
public:
WallBe(QHostAddress address, int port, QObject *parent = nullptr);
WallBe(const QHostAddress &address, int port, QObject *parent = nullptr);
~WallBe();
bool isAvailable();
bool connect();

View File

@ -6,12 +6,12 @@ QT += \
SOURCES += \
integrationpluginwallbe.cpp \
wallbe.cpp \
../modbus/modbustcpmaster.cpp \
host.cpp \
discover.cpp
HEADERS += \
integrationpluginwallbe.h \
wallbe.h \
../modbus/modbustcpmaster.h \
host.h \
discover.h