diff --git a/alphainnotec/README.md b/alphainnotec/README.md new file mode 100644 index 0000000..9cc8557 --- /dev/null +++ b/alphainnotec/README.md @@ -0,0 +1,23 @@ +# alpha innotec + +Connect nymea to alpha innotec heat pumps. + +In order to use the modbus interface, it is important to enable modbus on the heatpump using the Windows tools from Alpha Innotec. +The instructions for that can be found in the user manual of the heat pump. + +Please also make sure all values are configured as readable and writable for modbus, otherwise the heapump can only be monitored, but +not optimized for smart heating. + +## Supported Things + +* alpha connect + +## Requirements + +* The package 'nymea-plugin-alphainnotec' must be installed +* Both devices must be in the same local area network. +* Modbus enabled and all values are readable and writable. + +## More + +https://www.alpha-innotec.de diff --git a/alphainnotec/alpha-innotec.png b/alphainnotec/alpha-innotec.png new file mode 100644 index 0000000..3fd2fcc Binary files /dev/null and b/alphainnotec/alpha-innotec.png differ diff --git a/alphainnotec/alphaconnectmodbustcpconnection.cpp b/alphainnotec/alphaconnectmodbustcpconnection.cpp new file mode 100644 index 0000000..9f6dd9b --- /dev/null +++ b/alphainnotec/alphaconnectmodbustcpconnection.cpp @@ -0,0 +1,1273 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +* +* Copyright 2013 - 2021, nymea GmbH +* Contact: contact@nymea.io +* +* This fileDescriptor 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 . +* +* 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 "alphaconnectmodbustcpconnection.h" +#include "loggingcategories.h" + +NYMEA_LOGGING_CATEGORY(dcAlphaConnectModbusTcpConnection, "AlphaConnectModbusTcpConnection") + +AlphaConnectModbusTcpConnection::AlphaConnectModbusTcpConnection(const QHostAddress &hostAddress, uint port, quint16 slaveId, QObject *parent) : + ModbusTCPMaster(hostAddress, port, parent), + m_slaveId(slaveId) +{ + +} + +float AlphaConnectModbusTcpConnection::flowTemperature() const +{ + return m_flowTemperature; +} + +float AlphaConnectModbusTcpConnection::returnTemperature() const +{ + return m_returnTemperature; +} + +float AlphaConnectModbusTcpConnection::externalReturnTemperature() const +{ + return m_externalReturnTemperature; +} + +float AlphaConnectModbusTcpConnection::hotWaterTemperature() const +{ + return m_hotWaterTemperature; +} + +float AlphaConnectModbusTcpConnection::hotGasTemperature() const +{ + return m_hotGasTemperature; +} + +float AlphaConnectModbusTcpConnection::heatSourceInletTemperature() const +{ + return m_heatSourceInletTemperature; +} + +float AlphaConnectModbusTcpConnection::heatSourceOutletTemperature() const +{ + return m_heatSourceOutletTemperature; +} + +float AlphaConnectModbusTcpConnection::roomTemperature1() const +{ + return m_roomTemperature1; +} + +float AlphaConnectModbusTcpConnection::roomTemperature2() const +{ + return m_roomTemperature2; +} + +float AlphaConnectModbusTcpConnection::roomTemperature3() const +{ + return m_roomTemperature3; +} + +float AlphaConnectModbusTcpConnection::solarCollectorTemperature() const +{ + return m_solarCollectorTemperature; +} + +float AlphaConnectModbusTcpConnection::solarStorageTankTemperature() const +{ + return m_solarStorageTankTemperature; +} + +float AlphaConnectModbusTcpConnection::externalEnergySourceTemperature() const +{ + return m_externalEnergySourceTemperature; +} + +float AlphaConnectModbusTcpConnection::supplyAirTemperature() const +{ + return m_supplyAirTemperature; +} + +float AlphaConnectModbusTcpConnection::externalAirTemperature() const +{ + return m_externalAirTemperature; +} + +float AlphaConnectModbusTcpConnection::rbeRoomActualTemperature() const +{ + return m_rbeRoomActualTemperature; +} + +float AlphaConnectModbusTcpConnection::rbeRoomSetpointTemperature() const +{ + return m_rbeRoomSetpointTemperature; +} + +quint16 AlphaConnectModbusTcpConnection::heatingPumpOperatingHours() const +{ + return m_heatingPumpOperatingHours; +} + +AlphaConnectModbusTcpConnection::SystemStatus AlphaConnectModbusTcpConnection::systemStatus() const +{ + return m_systemStatus; +} + +float AlphaConnectModbusTcpConnection::heatingEnergy() const +{ + return m_heatingEnergy; +} + +float AlphaConnectModbusTcpConnection::waterHeatEnergy() const +{ + return m_waterHeatEnergy; +} + +float AlphaConnectModbusTcpConnection::totalHeatEnergy() const +{ + return m_totalHeatEnergy; +} + +float AlphaConnectModbusTcpConnection::outdoorTemperature() const +{ + return m_outdoorTemperature; +} + +QModbusReply *AlphaConnectModbusTcpConnection::setOutdoorTemperature(float outdoorTemperature) +{ + QVector values = ModbusDataUtils::convertFromUInt16(static_cast(outdoorTemperature * 1.0 / pow(10, -1))); + qCDebug(dcAlphaConnectModbusTcpConnection()) << "--> Write \"Outdoor temperature\" register:" << 0 << "size:" << 1 << values; + QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::HoldingRegisters, 0, values.count()); + request.setValues(values); + return sendWriteRequest(request, m_slaveId); +} + +float AlphaConnectModbusTcpConnection::returnSetpointTemperature() const +{ + return m_returnSetpointTemperature; +} + +QModbusReply *AlphaConnectModbusTcpConnection::setReturnSetpointTemperature(float returnSetpointTemperature) +{ + QVector values = ModbusDataUtils::convertFromUInt16(static_cast(returnSetpointTemperature * 1.0 / pow(10, -1))); + qCDebug(dcAlphaConnectModbusTcpConnection()) << "--> Write \"Return setpoint temperature\" register:" << 1 << "size:" << 1 << values; + QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::HoldingRegisters, 1, values.count()); + request.setValues(values); + return sendWriteRequest(request, m_slaveId); +} + +float AlphaConnectModbusTcpConnection::hotWaterSetpointTemperature() const +{ + return m_hotWaterSetpointTemperature; +} + +QModbusReply *AlphaConnectModbusTcpConnection::setHotWaterSetpointTemperature(float hotWaterSetpointTemperature) +{ + QVector values = ModbusDataUtils::convertFromUInt16(static_cast(hotWaterSetpointTemperature * 1.0 / pow(10, -1))); + qCDebug(dcAlphaConnectModbusTcpConnection()) << "--> Write \"Hot water setpoint temperature\" register:" << 5 << "size:" << 1 << values; + QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::HoldingRegisters, 5, values.count()); + request.setValues(values); + return sendWriteRequest(request, m_slaveId); +} + +AlphaConnectModbusTcpConnection::SmartGridState AlphaConnectModbusTcpConnection::smartGrid() const +{ + return m_smartGrid; +} + +QModbusReply *AlphaConnectModbusTcpConnection::setSmartGrid(SmartGridState smartGrid) +{ + QVector values = ModbusDataUtils::convertFromUInt16(static_cast(smartGrid)); + qCDebug(dcAlphaConnectModbusTcpConnection()) << "--> Write \"Smart grid control\" register:" << 14 << "size:" << 1 << values; + QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::HoldingRegisters, 14, values.count()); + request.setValues(values); + return sendWriteRequest(request, m_slaveId); +} + +void AlphaConnectModbusTcpConnection::initialize() +{ + // No init registers defined. Nothing to be done and we are finished. + emit initializationFinished(); +} + +void AlphaConnectModbusTcpConnection::update() +{ + updateFlowTemperature(); + updateReturnTemperature(); + updateExternalReturnTemperature(); + updateHotWaterTemperature(); + updateHotGasTemperature(); + updateHeatSourceInletTemperature(); + updateHeatSourceOutletTemperature(); + updateRoomTemperature1(); + updateRoomTemperature2(); + updateRoomTemperature3(); + updateSolarCollectorTemperature(); + updateSolarStorageTankTemperature(); + updateExternalEnergySourceTemperature(); + updateSupplyAirTemperature(); + updateExternalAirTemperature(); + updateRbeRoomActualTemperature(); + updateRbeRoomSetpointTemperature(); + updateHeatingPumpOperatingHours(); + updateSystemStatus(); + updateHeatingEnergy(); + updateWaterHeatEnergy(); + updateTotalHeatEnergy(); + updateOutdoorTemperature(); + updateReturnSetpointTemperature(); + updateHotWaterSetpointTemperature(); + updateSmartGrid(); +} + +void AlphaConnectModbusTcpConnection::updateFlowTemperature() +{ + // Update registers from Flow + qCDebug(dcAlphaConnectModbusTcpConnection()) << "--> Read \"Flow\" register:" << 1 << "size:" << 1; + QModbusReply *reply = readFlowTemperature(); + if (reply) { + if (!reply->isFinished()) { + connect(reply, &QModbusReply::finished, reply, &QModbusReply::deleteLater); + connect(reply, &QModbusReply::finished, this, [this, reply](){ + if (reply->error() == QModbusDevice::NoError) { + const QModbusDataUnit unit = reply->result(); + qCDebug(dcAlphaConnectModbusTcpConnection()) << "<-- Response from \"Flow\" register" << 1 << "size:" << 1 << unit.values(); + float receivedFlowTemperature = ModbusDataUtils::convertToUInt16(unit.values()) * 1.0 * pow(10, -1); + if (m_flowTemperature != receivedFlowTemperature) { + m_flowTemperature = receivedFlowTemperature; + emit flowTemperatureChanged(m_flowTemperature); + } + } + }); + + connect(reply, &QModbusReply::errorOccurred, this, [this, reply] (QModbusDevice::Error error){ + qCWarning(dcAlphaConnectModbusTcpConnection()) << "Modbus reply error occurred while updating \"Flow\" registers from" << hostAddress().toString() << error << reply->errorString(); + emit reply->finished(); // To make sure it will be deleted + }); + } else { + delete reply; // Broadcast reply returns immediatly + } + } else { + qCWarning(dcAlphaConnectModbusTcpConnection()) << "Error occurred while reading \"Flow\" registers from" << hostAddress().toString() << errorString(); + } +} + +void AlphaConnectModbusTcpConnection::updateReturnTemperature() +{ + // Update registers from Return + qCDebug(dcAlphaConnectModbusTcpConnection()) << "--> Read \"Return\" register:" << 2 << "size:" << 1; + QModbusReply *reply = readReturnTemperature(); + if (reply) { + if (!reply->isFinished()) { + connect(reply, &QModbusReply::finished, reply, &QModbusReply::deleteLater); + connect(reply, &QModbusReply::finished, this, [this, reply](){ + if (reply->error() == QModbusDevice::NoError) { + const QModbusDataUnit unit = reply->result(); + qCDebug(dcAlphaConnectModbusTcpConnection()) << "<-- Response from \"Return\" register" << 2 << "size:" << 1 << unit.values(); + float receivedReturnTemperature = ModbusDataUtils::convertToUInt16(unit.values()) * 1.0 * pow(10, -1); + if (m_returnTemperature != receivedReturnTemperature) { + m_returnTemperature = receivedReturnTemperature; + emit returnTemperatureChanged(m_returnTemperature); + } + } + }); + + connect(reply, &QModbusReply::errorOccurred, this, [this, reply] (QModbusDevice::Error error){ + qCWarning(dcAlphaConnectModbusTcpConnection()) << "Modbus reply error occurred while updating \"Return\" registers from" << hostAddress().toString() << error << reply->errorString(); + emit reply->finished(); // To make sure it will be deleted + }); + } else { + delete reply; // Broadcast reply returns immediatly + } + } else { + qCWarning(dcAlphaConnectModbusTcpConnection()) << "Error occurred while reading \"Return\" registers from" << hostAddress().toString() << errorString(); + } +} + +void AlphaConnectModbusTcpConnection::updateExternalReturnTemperature() +{ + // Update registers from External return + qCDebug(dcAlphaConnectModbusTcpConnection()) << "--> Read \"External return\" register:" << 3 << "size:" << 1; + QModbusReply *reply = readExternalReturnTemperature(); + if (reply) { + if (!reply->isFinished()) { + connect(reply, &QModbusReply::finished, reply, &QModbusReply::deleteLater); + connect(reply, &QModbusReply::finished, this, [this, reply](){ + if (reply->error() == QModbusDevice::NoError) { + const QModbusDataUnit unit = reply->result(); + qCDebug(dcAlphaConnectModbusTcpConnection()) << "<-- Response from \"External return\" register" << 3 << "size:" << 1 << unit.values(); + float receivedExternalReturnTemperature = ModbusDataUtils::convertToUInt16(unit.values()) * 1.0 * pow(10, -1); + if (m_externalReturnTemperature != receivedExternalReturnTemperature) { + m_externalReturnTemperature = receivedExternalReturnTemperature; + emit externalReturnTemperatureChanged(m_externalReturnTemperature); + } + } + }); + + connect(reply, &QModbusReply::errorOccurred, this, [this, reply] (QModbusDevice::Error error){ + qCWarning(dcAlphaConnectModbusTcpConnection()) << "Modbus reply error occurred while updating \"External return\" registers from" << hostAddress().toString() << error << reply->errorString(); + emit reply->finished(); // To make sure it will be deleted + }); + } else { + delete reply; // Broadcast reply returns immediatly + } + } else { + qCWarning(dcAlphaConnectModbusTcpConnection()) << "Error occurred while reading \"External return\" registers from" << hostAddress().toString() << errorString(); + } +} + +void AlphaConnectModbusTcpConnection::updateHotWaterTemperature() +{ + // Update registers from Hot water temperature + qCDebug(dcAlphaConnectModbusTcpConnection()) << "--> Read \"Hot water temperature\" register:" << 4 << "size:" << 1; + QModbusReply *reply = readHotWaterTemperature(); + if (reply) { + if (!reply->isFinished()) { + connect(reply, &QModbusReply::finished, reply, &QModbusReply::deleteLater); + connect(reply, &QModbusReply::finished, this, [this, reply](){ + if (reply->error() == QModbusDevice::NoError) { + const QModbusDataUnit unit = reply->result(); + qCDebug(dcAlphaConnectModbusTcpConnection()) << "<-- Response from \"Hot water temperature\" register" << 4 << "size:" << 1 << unit.values(); + float receivedHotWaterTemperature = ModbusDataUtils::convertToUInt16(unit.values()) * 1.0 * pow(10, -1); + if (m_hotWaterTemperature != receivedHotWaterTemperature) { + m_hotWaterTemperature = receivedHotWaterTemperature; + emit hotWaterTemperatureChanged(m_hotWaterTemperature); + } + } + }); + + connect(reply, &QModbusReply::errorOccurred, this, [this, reply] (QModbusDevice::Error error){ + qCWarning(dcAlphaConnectModbusTcpConnection()) << "Modbus reply error occurred while updating \"Hot water temperature\" registers from" << hostAddress().toString() << error << reply->errorString(); + emit reply->finished(); // To make sure it will be deleted + }); + } else { + delete reply; // Broadcast reply returns immediatly + } + } else { + qCWarning(dcAlphaConnectModbusTcpConnection()) << "Error occurred while reading \"Hot water temperature\" registers from" << hostAddress().toString() << errorString(); + } +} + +void AlphaConnectModbusTcpConnection::updateHotGasTemperature() +{ + // Update registers from Hot gas temperature + qCDebug(dcAlphaConnectModbusTcpConnection()) << "--> Read \"Hot gas temperature\" register:" << 8 << "size:" << 1; + QModbusReply *reply = readHotGasTemperature(); + if (reply) { + if (!reply->isFinished()) { + connect(reply, &QModbusReply::finished, reply, &QModbusReply::deleteLater); + connect(reply, &QModbusReply::finished, this, [this, reply](){ + if (reply->error() == QModbusDevice::NoError) { + const QModbusDataUnit unit = reply->result(); + qCDebug(dcAlphaConnectModbusTcpConnection()) << "<-- Response from \"Hot gas temperature\" register" << 8 << "size:" << 1 << unit.values(); + float receivedHotGasTemperature = ModbusDataUtils::convertToUInt16(unit.values()) * 1.0 * pow(10, -1); + if (m_hotGasTemperature != receivedHotGasTemperature) { + m_hotGasTemperature = receivedHotGasTemperature; + emit hotGasTemperatureChanged(m_hotGasTemperature); + } + } + }); + + connect(reply, &QModbusReply::errorOccurred, this, [this, reply] (QModbusDevice::Error error){ + qCWarning(dcAlphaConnectModbusTcpConnection()) << "Modbus reply error occurred while updating \"Hot gas temperature\" registers from" << hostAddress().toString() << error << reply->errorString(); + emit reply->finished(); // To make sure it will be deleted + }); + } else { + delete reply; // Broadcast reply returns immediatly + } + } else { + qCWarning(dcAlphaConnectModbusTcpConnection()) << "Error occurred while reading \"Hot gas temperature\" registers from" << hostAddress().toString() << errorString(); + } +} + +void AlphaConnectModbusTcpConnection::updateHeatSourceInletTemperature() +{ + // Update registers from Heat source inlet temperature + qCDebug(dcAlphaConnectModbusTcpConnection()) << "--> Read \"Heat source inlet temperature\" register:" << 9 << "size:" << 1; + QModbusReply *reply = readHeatSourceInletTemperature(); + if (reply) { + if (!reply->isFinished()) { + connect(reply, &QModbusReply::finished, reply, &QModbusReply::deleteLater); + connect(reply, &QModbusReply::finished, this, [this, reply](){ + if (reply->error() == QModbusDevice::NoError) { + const QModbusDataUnit unit = reply->result(); + qCDebug(dcAlphaConnectModbusTcpConnection()) << "<-- Response from \"Heat source inlet temperature\" register" << 9 << "size:" << 1 << unit.values(); + float receivedHeatSourceInletTemperature = ModbusDataUtils::convertToUInt16(unit.values()) * 1.0 * pow(10, -1); + if (m_heatSourceInletTemperature != receivedHeatSourceInletTemperature) { + m_heatSourceInletTemperature = receivedHeatSourceInletTemperature; + emit heatSourceInletTemperatureChanged(m_heatSourceInletTemperature); + } + } + }); + + connect(reply, &QModbusReply::errorOccurred, this, [this, reply] (QModbusDevice::Error error){ + qCWarning(dcAlphaConnectModbusTcpConnection()) << "Modbus reply error occurred while updating \"Heat source inlet temperature\" registers from" << hostAddress().toString() << error << reply->errorString(); + emit reply->finished(); // To make sure it will be deleted + }); + } else { + delete reply; // Broadcast reply returns immediatly + } + } else { + qCWarning(dcAlphaConnectModbusTcpConnection()) << "Error occurred while reading \"Heat source inlet temperature\" registers from" << hostAddress().toString() << errorString(); + } +} + +void AlphaConnectModbusTcpConnection::updateHeatSourceOutletTemperature() +{ + // Update registers from Heat source outlet temperature + qCDebug(dcAlphaConnectModbusTcpConnection()) << "--> Read \"Heat source outlet temperature\" register:" << 10 << "size:" << 1; + QModbusReply *reply = readHeatSourceOutletTemperature(); + if (reply) { + if (!reply->isFinished()) { + connect(reply, &QModbusReply::finished, reply, &QModbusReply::deleteLater); + connect(reply, &QModbusReply::finished, this, [this, reply](){ + if (reply->error() == QModbusDevice::NoError) { + const QModbusDataUnit unit = reply->result(); + qCDebug(dcAlphaConnectModbusTcpConnection()) << "<-- Response from \"Heat source outlet temperature\" register" << 10 << "size:" << 1 << unit.values(); + float receivedHeatSourceOutletTemperature = ModbusDataUtils::convertToUInt16(unit.values()) * 1.0 * pow(10, -1); + if (m_heatSourceOutletTemperature != receivedHeatSourceOutletTemperature) { + m_heatSourceOutletTemperature = receivedHeatSourceOutletTemperature; + emit heatSourceOutletTemperatureChanged(m_heatSourceOutletTemperature); + } + } + }); + + connect(reply, &QModbusReply::errorOccurred, this, [this, reply] (QModbusDevice::Error error){ + qCWarning(dcAlphaConnectModbusTcpConnection()) << "Modbus reply error occurred while updating \"Heat source outlet temperature\" registers from" << hostAddress().toString() << error << reply->errorString(); + emit reply->finished(); // To make sure it will be deleted + }); + } else { + delete reply; // Broadcast reply returns immediatly + } + } else { + qCWarning(dcAlphaConnectModbusTcpConnection()) << "Error occurred while reading \"Heat source outlet temperature\" registers from" << hostAddress().toString() << errorString(); + } +} + +void AlphaConnectModbusTcpConnection::updateRoomTemperature1() +{ + // Update registers from Room remote adjuster 1 temperature + qCDebug(dcAlphaConnectModbusTcpConnection()) << "--> Read \"Room remote adjuster 1 temperature\" register:" << 11 << "size:" << 1; + QModbusReply *reply = readRoomTemperature1(); + if (reply) { + if (!reply->isFinished()) { + connect(reply, &QModbusReply::finished, reply, &QModbusReply::deleteLater); + connect(reply, &QModbusReply::finished, this, [this, reply](){ + if (reply->error() == QModbusDevice::NoError) { + const QModbusDataUnit unit = reply->result(); + qCDebug(dcAlphaConnectModbusTcpConnection()) << "<-- Response from \"Room remote adjuster 1 temperature\" register" << 11 << "size:" << 1 << unit.values(); + float receivedRoomTemperature1 = ModbusDataUtils::convertToUInt16(unit.values()) * 1.0 * pow(10, -1); + if (m_roomTemperature1 != receivedRoomTemperature1) { + m_roomTemperature1 = receivedRoomTemperature1; + emit roomTemperature1Changed(m_roomTemperature1); + } + } + }); + + connect(reply, &QModbusReply::errorOccurred, this, [this, reply] (QModbusDevice::Error error){ + qCWarning(dcAlphaConnectModbusTcpConnection()) << "Modbus reply error occurred while updating \"Room remote adjuster 1 temperature\" registers from" << hostAddress().toString() << error << reply->errorString(); + emit reply->finished(); // To make sure it will be deleted + }); + } else { + delete reply; // Broadcast reply returns immediatly + } + } else { + qCWarning(dcAlphaConnectModbusTcpConnection()) << "Error occurred while reading \"Room remote adjuster 1 temperature\" registers from" << hostAddress().toString() << errorString(); + } +} + +void AlphaConnectModbusTcpConnection::updateRoomTemperature2() +{ + // Update registers from Room remote adjuster 2 temperature + qCDebug(dcAlphaConnectModbusTcpConnection()) << "--> Read \"Room remote adjuster 2 temperature\" register:" << 12 << "size:" << 1; + QModbusReply *reply = readRoomTemperature2(); + if (reply) { + if (!reply->isFinished()) { + connect(reply, &QModbusReply::finished, reply, &QModbusReply::deleteLater); + connect(reply, &QModbusReply::finished, this, [this, reply](){ + if (reply->error() == QModbusDevice::NoError) { + const QModbusDataUnit unit = reply->result(); + qCDebug(dcAlphaConnectModbusTcpConnection()) << "<-- Response from \"Room remote adjuster 2 temperature\" register" << 12 << "size:" << 1 << unit.values(); + float receivedRoomTemperature2 = ModbusDataUtils::convertToUInt16(unit.values()) * 1.0 * pow(10, -1); + if (m_roomTemperature2 != receivedRoomTemperature2) { + m_roomTemperature2 = receivedRoomTemperature2; + emit roomTemperature2Changed(m_roomTemperature2); + } + } + }); + + connect(reply, &QModbusReply::errorOccurred, this, [this, reply] (QModbusDevice::Error error){ + qCWarning(dcAlphaConnectModbusTcpConnection()) << "Modbus reply error occurred while updating \"Room remote adjuster 2 temperature\" registers from" << hostAddress().toString() << error << reply->errorString(); + emit reply->finished(); // To make sure it will be deleted + }); + } else { + delete reply; // Broadcast reply returns immediatly + } + } else { + qCWarning(dcAlphaConnectModbusTcpConnection()) << "Error occurred while reading \"Room remote adjuster 2 temperature\" registers from" << hostAddress().toString() << errorString(); + } +} + +void AlphaConnectModbusTcpConnection::updateRoomTemperature3() +{ + // Update registers from Room remote adjuster 3 temperature + qCDebug(dcAlphaConnectModbusTcpConnection()) << "--> Read \"Room remote adjuster 3 temperature\" register:" << 13 << "size:" << 1; + QModbusReply *reply = readRoomTemperature3(); + if (reply) { + if (!reply->isFinished()) { + connect(reply, &QModbusReply::finished, reply, &QModbusReply::deleteLater); + connect(reply, &QModbusReply::finished, this, [this, reply](){ + if (reply->error() == QModbusDevice::NoError) { + const QModbusDataUnit unit = reply->result(); + qCDebug(dcAlphaConnectModbusTcpConnection()) << "<-- Response from \"Room remote adjuster 3 temperature\" register" << 13 << "size:" << 1 << unit.values(); + float receivedRoomTemperature3 = ModbusDataUtils::convertToUInt16(unit.values()) * 1.0 * pow(10, -1); + if (m_roomTemperature3 != receivedRoomTemperature3) { + m_roomTemperature3 = receivedRoomTemperature3; + emit roomTemperature3Changed(m_roomTemperature3); + } + } + }); + + connect(reply, &QModbusReply::errorOccurred, this, [this, reply] (QModbusDevice::Error error){ + qCWarning(dcAlphaConnectModbusTcpConnection()) << "Modbus reply error occurred while updating \"Room remote adjuster 3 temperature\" registers from" << hostAddress().toString() << error << reply->errorString(); + emit reply->finished(); // To make sure it will be deleted + }); + } else { + delete reply; // Broadcast reply returns immediatly + } + } else { + qCWarning(dcAlphaConnectModbusTcpConnection()) << "Error occurred while reading \"Room remote adjuster 3 temperature\" registers from" << hostAddress().toString() << errorString(); + } +} + +void AlphaConnectModbusTcpConnection::updateSolarCollectorTemperature() +{ + // Update registers from Solar collector temperature + qCDebug(dcAlphaConnectModbusTcpConnection()) << "--> Read \"Solar collector temperature\" register:" << 14 << "size:" << 1; + QModbusReply *reply = readSolarCollectorTemperature(); + if (reply) { + if (!reply->isFinished()) { + connect(reply, &QModbusReply::finished, reply, &QModbusReply::deleteLater); + connect(reply, &QModbusReply::finished, this, [this, reply](){ + if (reply->error() == QModbusDevice::NoError) { + const QModbusDataUnit unit = reply->result(); + qCDebug(dcAlphaConnectModbusTcpConnection()) << "<-- Response from \"Solar collector temperature\" register" << 14 << "size:" << 1 << unit.values(); + float receivedSolarCollectorTemperature = ModbusDataUtils::convertToUInt16(unit.values()) * 1.0 * pow(10, -1); + if (m_solarCollectorTemperature != receivedSolarCollectorTemperature) { + m_solarCollectorTemperature = receivedSolarCollectorTemperature; + emit solarCollectorTemperatureChanged(m_solarCollectorTemperature); + } + } + }); + + connect(reply, &QModbusReply::errorOccurred, this, [this, reply] (QModbusDevice::Error error){ + qCWarning(dcAlphaConnectModbusTcpConnection()) << "Modbus reply error occurred while updating \"Solar collector temperature\" registers from" << hostAddress().toString() << error << reply->errorString(); + emit reply->finished(); // To make sure it will be deleted + }); + } else { + delete reply; // Broadcast reply returns immediatly + } + } else { + qCWarning(dcAlphaConnectModbusTcpConnection()) << "Error occurred while reading \"Solar collector temperature\" registers from" << hostAddress().toString() << errorString(); + } +} + +void AlphaConnectModbusTcpConnection::updateSolarStorageTankTemperature() +{ + // Update registers from Solar storage tank temperature + qCDebug(dcAlphaConnectModbusTcpConnection()) << "--> Read \"Solar storage tank temperature\" register:" << 15 << "size:" << 1; + QModbusReply *reply = readSolarStorageTankTemperature(); + if (reply) { + if (!reply->isFinished()) { + connect(reply, &QModbusReply::finished, reply, &QModbusReply::deleteLater); + connect(reply, &QModbusReply::finished, this, [this, reply](){ + if (reply->error() == QModbusDevice::NoError) { + const QModbusDataUnit unit = reply->result(); + qCDebug(dcAlphaConnectModbusTcpConnection()) << "<-- Response from \"Solar storage tank temperature\" register" << 15 << "size:" << 1 << unit.values(); + float receivedSolarStorageTankTemperature = ModbusDataUtils::convertToUInt16(unit.values()) * 1.0 * pow(10, -1); + if (m_solarStorageTankTemperature != receivedSolarStorageTankTemperature) { + m_solarStorageTankTemperature = receivedSolarStorageTankTemperature; + emit solarStorageTankTemperatureChanged(m_solarStorageTankTemperature); + } + } + }); + + connect(reply, &QModbusReply::errorOccurred, this, [this, reply] (QModbusDevice::Error error){ + qCWarning(dcAlphaConnectModbusTcpConnection()) << "Modbus reply error occurred while updating \"Solar storage tank temperature\" registers from" << hostAddress().toString() << error << reply->errorString(); + emit reply->finished(); // To make sure it will be deleted + }); + } else { + delete reply; // Broadcast reply returns immediatly + } + } else { + qCWarning(dcAlphaConnectModbusTcpConnection()) << "Error occurred while reading \"Solar storage tank temperature\" registers from" << hostAddress().toString() << errorString(); + } +} + +void AlphaConnectModbusTcpConnection::updateExternalEnergySourceTemperature() +{ + // Update registers from External energy source temperature + qCDebug(dcAlphaConnectModbusTcpConnection()) << "--> Read \"External energy source temperature\" register:" << 16 << "size:" << 1; + QModbusReply *reply = readExternalEnergySourceTemperature(); + if (reply) { + if (!reply->isFinished()) { + connect(reply, &QModbusReply::finished, reply, &QModbusReply::deleteLater); + connect(reply, &QModbusReply::finished, this, [this, reply](){ + if (reply->error() == QModbusDevice::NoError) { + const QModbusDataUnit unit = reply->result(); + qCDebug(dcAlphaConnectModbusTcpConnection()) << "<-- Response from \"External energy source temperature\" register" << 16 << "size:" << 1 << unit.values(); + float receivedExternalEnergySourceTemperature = ModbusDataUtils::convertToUInt16(unit.values()) * 1.0 * pow(10, -1); + if (m_externalEnergySourceTemperature != receivedExternalEnergySourceTemperature) { + m_externalEnergySourceTemperature = receivedExternalEnergySourceTemperature; + emit externalEnergySourceTemperatureChanged(m_externalEnergySourceTemperature); + } + } + }); + + connect(reply, &QModbusReply::errorOccurred, this, [this, reply] (QModbusDevice::Error error){ + qCWarning(dcAlphaConnectModbusTcpConnection()) << "Modbus reply error occurred while updating \"External energy source temperature\" registers from" << hostAddress().toString() << error << reply->errorString(); + emit reply->finished(); // To make sure it will be deleted + }); + } else { + delete reply; // Broadcast reply returns immediatly + } + } else { + qCWarning(dcAlphaConnectModbusTcpConnection()) << "Error occurred while reading \"External energy source temperature\" registers from" << hostAddress().toString() << errorString(); + } +} + +void AlphaConnectModbusTcpConnection::updateSupplyAirTemperature() +{ + // Update registers from Supply air temperature + qCDebug(dcAlphaConnectModbusTcpConnection()) << "--> Read \"Supply air temperature\" register:" << 17 << "size:" << 1; + QModbusReply *reply = readSupplyAirTemperature(); + if (reply) { + if (!reply->isFinished()) { + connect(reply, &QModbusReply::finished, reply, &QModbusReply::deleteLater); + connect(reply, &QModbusReply::finished, this, [this, reply](){ + if (reply->error() == QModbusDevice::NoError) { + const QModbusDataUnit unit = reply->result(); + qCDebug(dcAlphaConnectModbusTcpConnection()) << "<-- Response from \"Supply air temperature\" register" << 17 << "size:" << 1 << unit.values(); + float receivedSupplyAirTemperature = ModbusDataUtils::convertToUInt16(unit.values()) * 1.0 * pow(10, -1); + if (m_supplyAirTemperature != receivedSupplyAirTemperature) { + m_supplyAirTemperature = receivedSupplyAirTemperature; + emit supplyAirTemperatureChanged(m_supplyAirTemperature); + } + } + }); + + connect(reply, &QModbusReply::errorOccurred, this, [this, reply] (QModbusDevice::Error error){ + qCWarning(dcAlphaConnectModbusTcpConnection()) << "Modbus reply error occurred while updating \"Supply air temperature\" registers from" << hostAddress().toString() << error << reply->errorString(); + emit reply->finished(); // To make sure it will be deleted + }); + } else { + delete reply; // Broadcast reply returns immediatly + } + } else { + qCWarning(dcAlphaConnectModbusTcpConnection()) << "Error occurred while reading \"Supply air temperature\" registers from" << hostAddress().toString() << errorString(); + } +} + +void AlphaConnectModbusTcpConnection::updateExternalAirTemperature() +{ + // Update registers from External air temperature + qCDebug(dcAlphaConnectModbusTcpConnection()) << "--> Read \"External air temperature\" register:" << 18 << "size:" << 1; + QModbusReply *reply = readExternalAirTemperature(); + if (reply) { + if (!reply->isFinished()) { + connect(reply, &QModbusReply::finished, reply, &QModbusReply::deleteLater); + connect(reply, &QModbusReply::finished, this, [this, reply](){ + if (reply->error() == QModbusDevice::NoError) { + const QModbusDataUnit unit = reply->result(); + qCDebug(dcAlphaConnectModbusTcpConnection()) << "<-- Response from \"External air temperature\" register" << 18 << "size:" << 1 << unit.values(); + float receivedExternalAirTemperature = ModbusDataUtils::convertToUInt16(unit.values()) * 1.0 * pow(10, -1); + if (m_externalAirTemperature != receivedExternalAirTemperature) { + m_externalAirTemperature = receivedExternalAirTemperature; + emit externalAirTemperatureChanged(m_externalAirTemperature); + } + } + }); + + connect(reply, &QModbusReply::errorOccurred, this, [this, reply] (QModbusDevice::Error error){ + qCWarning(dcAlphaConnectModbusTcpConnection()) << "Modbus reply error occurred while updating \"External air temperature\" registers from" << hostAddress().toString() << error << reply->errorString(); + emit reply->finished(); // To make sure it will be deleted + }); + } else { + delete reply; // Broadcast reply returns immediatly + } + } else { + qCWarning(dcAlphaConnectModbusTcpConnection()) << "Error occurred while reading \"External air temperature\" registers from" << hostAddress().toString() << errorString(); + } +} + +void AlphaConnectModbusTcpConnection::updateRbeRoomActualTemperature() +{ + // Update registers from RBE actual room temperature + qCDebug(dcAlphaConnectModbusTcpConnection()) << "--> Read \"RBE actual room temperature\" register:" << 24 << "size:" << 1; + QModbusReply *reply = readRbeRoomActualTemperature(); + if (reply) { + if (!reply->isFinished()) { + connect(reply, &QModbusReply::finished, reply, &QModbusReply::deleteLater); + connect(reply, &QModbusReply::finished, this, [this, reply](){ + if (reply->error() == QModbusDevice::NoError) { + const QModbusDataUnit unit = reply->result(); + qCDebug(dcAlphaConnectModbusTcpConnection()) << "<-- Response from \"RBE actual room temperature\" register" << 24 << "size:" << 1 << unit.values(); + float receivedRbeRoomActualTemperature = ModbusDataUtils::convertToUInt16(unit.values()) * 1.0 * pow(10, -1); + if (m_rbeRoomActualTemperature != receivedRbeRoomActualTemperature) { + m_rbeRoomActualTemperature = receivedRbeRoomActualTemperature; + emit rbeRoomActualTemperatureChanged(m_rbeRoomActualTemperature); + } + } + }); + + connect(reply, &QModbusReply::errorOccurred, this, [this, reply] (QModbusDevice::Error error){ + qCWarning(dcAlphaConnectModbusTcpConnection()) << "Modbus reply error occurred while updating \"RBE actual room temperature\" registers from" << hostAddress().toString() << error << reply->errorString(); + emit reply->finished(); // To make sure it will be deleted + }); + } else { + delete reply; // Broadcast reply returns immediatly + } + } else { + qCWarning(dcAlphaConnectModbusTcpConnection()) << "Error occurred while reading \"RBE actual room temperature\" registers from" << hostAddress().toString() << errorString(); + } +} + +void AlphaConnectModbusTcpConnection::updateRbeRoomSetpointTemperature() +{ + // Update registers from RBE room temperature setpoint + qCDebug(dcAlphaConnectModbusTcpConnection()) << "--> Read \"RBE room temperature setpoint\" register:" << 24 << "size:" << 1; + QModbusReply *reply = readRbeRoomSetpointTemperature(); + if (reply) { + if (!reply->isFinished()) { + connect(reply, &QModbusReply::finished, reply, &QModbusReply::deleteLater); + connect(reply, &QModbusReply::finished, this, [this, reply](){ + if (reply->error() == QModbusDevice::NoError) { + const QModbusDataUnit unit = reply->result(); + qCDebug(dcAlphaConnectModbusTcpConnection()) << "<-- Response from \"RBE room temperature setpoint\" register" << 24 << "size:" << 1 << unit.values(); + float receivedRbeRoomSetpointTemperature = ModbusDataUtils::convertToUInt16(unit.values()) * 1.0 * pow(10, -1); + if (m_rbeRoomSetpointTemperature != receivedRbeRoomSetpointTemperature) { + m_rbeRoomSetpointTemperature = receivedRbeRoomSetpointTemperature; + emit rbeRoomSetpointTemperatureChanged(m_rbeRoomSetpointTemperature); + } + } + }); + + connect(reply, &QModbusReply::errorOccurred, this, [this, reply] (QModbusDevice::Error error){ + qCWarning(dcAlphaConnectModbusTcpConnection()) << "Modbus reply error occurred while updating \"RBE room temperature setpoint\" registers from" << hostAddress().toString() << error << reply->errorString(); + emit reply->finished(); // To make sure it will be deleted + }); + } else { + delete reply; // Broadcast reply returns immediatly + } + } else { + qCWarning(dcAlphaConnectModbusTcpConnection()) << "Error occurred while reading \"RBE room temperature setpoint\" registers from" << hostAddress().toString() << errorString(); + } +} + +void AlphaConnectModbusTcpConnection::updateHeatingPumpOperatingHours() +{ + // Update registers from Heating pump operating hours + qCDebug(dcAlphaConnectModbusTcpConnection()) << "--> Read \"Heating pump operating hours\" register:" << 33 << "size:" << 1; + QModbusReply *reply = readHeatingPumpOperatingHours(); + if (reply) { + if (!reply->isFinished()) { + connect(reply, &QModbusReply::finished, reply, &QModbusReply::deleteLater); + connect(reply, &QModbusReply::finished, this, [this, reply](){ + if (reply->error() == QModbusDevice::NoError) { + const QModbusDataUnit unit = reply->result(); + qCDebug(dcAlphaConnectModbusTcpConnection()) << "<-- Response from \"Heating pump operating hours\" register" << 33 << "size:" << 1 << unit.values(); + quint16 receivedHeatingPumpOperatingHours = ModbusDataUtils::convertToUInt16(unit.values()); + if (m_heatingPumpOperatingHours != receivedHeatingPumpOperatingHours) { + m_heatingPumpOperatingHours = receivedHeatingPumpOperatingHours; + emit heatingPumpOperatingHoursChanged(m_heatingPumpOperatingHours); + } + } + }); + + connect(reply, &QModbusReply::errorOccurred, this, [this, reply] (QModbusDevice::Error error){ + qCWarning(dcAlphaConnectModbusTcpConnection()) << "Modbus reply error occurred while updating \"Heating pump operating hours\" registers from" << hostAddress().toString() << error << reply->errorString(); + emit reply->finished(); // To make sure it will be deleted + }); + } else { + delete reply; // Broadcast reply returns immediatly + } + } else { + qCWarning(dcAlphaConnectModbusTcpConnection()) << "Error occurred while reading \"Heating pump operating hours\" registers from" << hostAddress().toString() << errorString(); + } +} + +void AlphaConnectModbusTcpConnection::updateSystemStatus() +{ + // Update registers from System status + qCDebug(dcAlphaConnectModbusTcpConnection()) << "--> Read \"System status\" register:" << 37 << "size:" << 1; + QModbusReply *reply = readSystemStatus(); + if (reply) { + if (!reply->isFinished()) { + connect(reply, &QModbusReply::finished, reply, &QModbusReply::deleteLater); + connect(reply, &QModbusReply::finished, this, [this, reply](){ + if (reply->error() == QModbusDevice::NoError) { + const QModbusDataUnit unit = reply->result(); + qCDebug(dcAlphaConnectModbusTcpConnection()) << "<-- Response from \"System status\" register" << 37 << "size:" << 1 << unit.values(); + SystemStatus receivedSystemStatus = static_cast(ModbusDataUtils::convertToUInt16(unit.values())); + if (m_systemStatus != receivedSystemStatus) { + m_systemStatus = receivedSystemStatus; + emit systemStatusChanged(m_systemStatus); + } + } + }); + + connect(reply, &QModbusReply::errorOccurred, this, [this, reply] (QModbusDevice::Error error){ + qCWarning(dcAlphaConnectModbusTcpConnection()) << "Modbus reply error occurred while updating \"System status\" registers from" << hostAddress().toString() << error << reply->errorString(); + emit reply->finished(); // To make sure it will be deleted + }); + } else { + delete reply; // Broadcast reply returns immediatly + } + } else { + qCWarning(dcAlphaConnectModbusTcpConnection()) << "Error occurred while reading \"System status\" registers from" << hostAddress().toString() << errorString(); + } +} + +void AlphaConnectModbusTcpConnection::updateHeatingEnergy() +{ + // Update registers from Heating energy + qCDebug(dcAlphaConnectModbusTcpConnection()) << "--> Read \"Heating energy\" register:" << 38 << "size:" << 2; + QModbusReply *reply = readHeatingEnergy(); + if (reply) { + if (!reply->isFinished()) { + connect(reply, &QModbusReply::finished, reply, &QModbusReply::deleteLater); + connect(reply, &QModbusReply::finished, this, [this, reply](){ + if (reply->error() == QModbusDevice::NoError) { + const QModbusDataUnit unit = reply->result(); + qCDebug(dcAlphaConnectModbusTcpConnection()) << "<-- Response from \"Heating energy\" register" << 38 << "size:" << 2 << unit.values(); + float receivedHeatingEnergy = ModbusDataUtils::convertToUInt32(unit.values(), ModbusDataUtils::ByteOrderBigEndian) * 1.0 * pow(10, -1); + if (m_heatingEnergy != receivedHeatingEnergy) { + m_heatingEnergy = receivedHeatingEnergy; + emit heatingEnergyChanged(m_heatingEnergy); + } + } + }); + + connect(reply, &QModbusReply::errorOccurred, this, [this, reply] (QModbusDevice::Error error){ + qCWarning(dcAlphaConnectModbusTcpConnection()) << "Modbus reply error occurred while updating \"Heating energy\" registers from" << hostAddress().toString() << error << reply->errorString(); + emit reply->finished(); // To make sure it will be deleted + }); + } else { + delete reply; // Broadcast reply returns immediatly + } + } else { + qCWarning(dcAlphaConnectModbusTcpConnection()) << "Error occurred while reading \"Heating energy\" registers from" << hostAddress().toString() << errorString(); + } +} + +void AlphaConnectModbusTcpConnection::updateWaterHeatEnergy() +{ + // Update registers from Water heat energy + qCDebug(dcAlphaConnectModbusTcpConnection()) << "--> Read \"Water heat energy\" register:" << 40 << "size:" << 2; + QModbusReply *reply = readWaterHeatEnergy(); + if (reply) { + if (!reply->isFinished()) { + connect(reply, &QModbusReply::finished, reply, &QModbusReply::deleteLater); + connect(reply, &QModbusReply::finished, this, [this, reply](){ + if (reply->error() == QModbusDevice::NoError) { + const QModbusDataUnit unit = reply->result(); + qCDebug(dcAlphaConnectModbusTcpConnection()) << "<-- Response from \"Water heat energy\" register" << 40 << "size:" << 2 << unit.values(); + float receivedWaterHeatEnergy = ModbusDataUtils::convertToUInt32(unit.values(), ModbusDataUtils::ByteOrderBigEndian) * 1.0 * pow(10, -1); + if (m_waterHeatEnergy != receivedWaterHeatEnergy) { + m_waterHeatEnergy = receivedWaterHeatEnergy; + emit waterHeatEnergyChanged(m_waterHeatEnergy); + } + } + }); + + connect(reply, &QModbusReply::errorOccurred, this, [this, reply] (QModbusDevice::Error error){ + qCWarning(dcAlphaConnectModbusTcpConnection()) << "Modbus reply error occurred while updating \"Water heat energy\" registers from" << hostAddress().toString() << error << reply->errorString(); + emit reply->finished(); // To make sure it will be deleted + }); + } else { + delete reply; // Broadcast reply returns immediatly + } + } else { + qCWarning(dcAlphaConnectModbusTcpConnection()) << "Error occurred while reading \"Water heat energy\" registers from" << hostAddress().toString() << errorString(); + } +} + +void AlphaConnectModbusTcpConnection::updateTotalHeatEnergy() +{ + // Update registers from Total energy + qCDebug(dcAlphaConnectModbusTcpConnection()) << "--> Read \"Total energy\" register:" << 44 << "size:" << 2; + QModbusReply *reply = readTotalHeatEnergy(); + if (reply) { + if (!reply->isFinished()) { + connect(reply, &QModbusReply::finished, reply, &QModbusReply::deleteLater); + connect(reply, &QModbusReply::finished, this, [this, reply](){ + if (reply->error() == QModbusDevice::NoError) { + const QModbusDataUnit unit = reply->result(); + qCDebug(dcAlphaConnectModbusTcpConnection()) << "<-- Response from \"Total energy\" register" << 44 << "size:" << 2 << unit.values(); + float receivedTotalHeatEnergy = ModbusDataUtils::convertToUInt32(unit.values(), ModbusDataUtils::ByteOrderBigEndian) * 1.0 * pow(10, -1); + if (m_totalHeatEnergy != receivedTotalHeatEnergy) { + m_totalHeatEnergy = receivedTotalHeatEnergy; + emit totalHeatEnergyChanged(m_totalHeatEnergy); + } + } + }); + + connect(reply, &QModbusReply::errorOccurred, this, [this, reply] (QModbusDevice::Error error){ + qCWarning(dcAlphaConnectModbusTcpConnection()) << "Modbus reply error occurred while updating \"Total energy\" registers from" << hostAddress().toString() << error << reply->errorString(); + emit reply->finished(); // To make sure it will be deleted + }); + } else { + delete reply; // Broadcast reply returns immediatly + } + } else { + qCWarning(dcAlphaConnectModbusTcpConnection()) << "Error occurred while reading \"Total energy\" registers from" << hostAddress().toString() << errorString(); + } +} + +void AlphaConnectModbusTcpConnection::updateOutdoorTemperature() +{ + // Update registers from Outdoor temperature + qCDebug(dcAlphaConnectModbusTcpConnection()) << "--> Read \"Outdoor temperature\" register:" << 0 << "size:" << 1; + QModbusReply *reply = readOutdoorTemperature(); + if (reply) { + if (!reply->isFinished()) { + connect(reply, &QModbusReply::finished, reply, &QModbusReply::deleteLater); + connect(reply, &QModbusReply::finished, this, [this, reply](){ + if (reply->error() == QModbusDevice::NoError) { + const QModbusDataUnit unit = reply->result(); + qCDebug(dcAlphaConnectModbusTcpConnection()) << "<-- Response from \"Outdoor temperature\" register" << 0 << "size:" << 1 << unit.values(); + float receivedOutdoorTemperature = ModbusDataUtils::convertToUInt16(unit.values()) * 1.0 * pow(10, -1); + if (m_outdoorTemperature != receivedOutdoorTemperature) { + m_outdoorTemperature = receivedOutdoorTemperature; + emit outdoorTemperatureChanged(m_outdoorTemperature); + } + } + }); + + connect(reply, &QModbusReply::errorOccurred, this, [this, reply] (QModbusDevice::Error error){ + qCWarning(dcAlphaConnectModbusTcpConnection()) << "Modbus reply error occurred while updating \"Outdoor temperature\" registers from" << hostAddress().toString() << error << reply->errorString(); + emit reply->finished(); // To make sure it will be deleted + }); + } else { + delete reply; // Broadcast reply returns immediatly + } + } else { + qCWarning(dcAlphaConnectModbusTcpConnection()) << "Error occurred while reading \"Outdoor temperature\" registers from" << hostAddress().toString() << errorString(); + } +} + +void AlphaConnectModbusTcpConnection::updateReturnSetpointTemperature() +{ + // Update registers from Return setpoint temperature + qCDebug(dcAlphaConnectModbusTcpConnection()) << "--> Read \"Return setpoint temperature\" register:" << 1 << "size:" << 1; + QModbusReply *reply = readReturnSetpointTemperature(); + if (reply) { + if (!reply->isFinished()) { + connect(reply, &QModbusReply::finished, reply, &QModbusReply::deleteLater); + connect(reply, &QModbusReply::finished, this, [this, reply](){ + if (reply->error() == QModbusDevice::NoError) { + const QModbusDataUnit unit = reply->result(); + qCDebug(dcAlphaConnectModbusTcpConnection()) << "<-- Response from \"Return setpoint temperature\" register" << 1 << "size:" << 1 << unit.values(); + float receivedReturnSetpointTemperature = ModbusDataUtils::convertToUInt16(unit.values()) * 1.0 * pow(10, -1); + if (m_returnSetpointTemperature != receivedReturnSetpointTemperature) { + m_returnSetpointTemperature = receivedReturnSetpointTemperature; + emit returnSetpointTemperatureChanged(m_returnSetpointTemperature); + } + } + }); + + connect(reply, &QModbusReply::errorOccurred, this, [this, reply] (QModbusDevice::Error error){ + qCWarning(dcAlphaConnectModbusTcpConnection()) << "Modbus reply error occurred while updating \"Return setpoint temperature\" registers from" << hostAddress().toString() << error << reply->errorString(); + emit reply->finished(); // To make sure it will be deleted + }); + } else { + delete reply; // Broadcast reply returns immediatly + } + } else { + qCWarning(dcAlphaConnectModbusTcpConnection()) << "Error occurred while reading \"Return setpoint temperature\" registers from" << hostAddress().toString() << errorString(); + } +} + +void AlphaConnectModbusTcpConnection::updateHotWaterSetpointTemperature() +{ + // Update registers from Hot water setpoint temperature + qCDebug(dcAlphaConnectModbusTcpConnection()) << "--> Read \"Hot water setpoint temperature\" register:" << 5 << "size:" << 1; + QModbusReply *reply = readHotWaterSetpointTemperature(); + if (reply) { + if (!reply->isFinished()) { + connect(reply, &QModbusReply::finished, reply, &QModbusReply::deleteLater); + connect(reply, &QModbusReply::finished, this, [this, reply](){ + if (reply->error() == QModbusDevice::NoError) { + const QModbusDataUnit unit = reply->result(); + qCDebug(dcAlphaConnectModbusTcpConnection()) << "<-- Response from \"Hot water setpoint temperature\" register" << 5 << "size:" << 1 << unit.values(); + float receivedHotWaterSetpointTemperature = ModbusDataUtils::convertToUInt16(unit.values()) * 1.0 * pow(10, -1); + if (m_hotWaterSetpointTemperature != receivedHotWaterSetpointTemperature) { + m_hotWaterSetpointTemperature = receivedHotWaterSetpointTemperature; + emit hotWaterSetpointTemperatureChanged(m_hotWaterSetpointTemperature); + } + } + }); + + connect(reply, &QModbusReply::errorOccurred, this, [this, reply] (QModbusDevice::Error error){ + qCWarning(dcAlphaConnectModbusTcpConnection()) << "Modbus reply error occurred while updating \"Hot water setpoint temperature\" registers from" << hostAddress().toString() << error << reply->errorString(); + emit reply->finished(); // To make sure it will be deleted + }); + } else { + delete reply; // Broadcast reply returns immediatly + } + } else { + qCWarning(dcAlphaConnectModbusTcpConnection()) << "Error occurred while reading \"Hot water setpoint temperature\" registers from" << hostAddress().toString() << errorString(); + } +} + +void AlphaConnectModbusTcpConnection::updateSmartGrid() +{ + // Update registers from Smart grid control + qCDebug(dcAlphaConnectModbusTcpConnection()) << "--> Read \"Smart grid control\" register:" << 14 << "size:" << 1; + QModbusReply *reply = readSmartGrid(); + if (reply) { + if (!reply->isFinished()) { + connect(reply, &QModbusReply::finished, reply, &QModbusReply::deleteLater); + connect(reply, &QModbusReply::finished, this, [this, reply](){ + if (reply->error() == QModbusDevice::NoError) { + const QModbusDataUnit unit = reply->result(); + qCDebug(dcAlphaConnectModbusTcpConnection()) << "<-- Response from \"Smart grid control\" register" << 14 << "size:" << 1 << unit.values(); + SmartGridState receivedSmartGrid = static_cast(ModbusDataUtils::convertToUInt16(unit.values())); + if (m_smartGrid != receivedSmartGrid) { + m_smartGrid = receivedSmartGrid; + emit smartGridChanged(m_smartGrid); + } + } + }); + + connect(reply, &QModbusReply::errorOccurred, this, [this, reply] (QModbusDevice::Error error){ + qCWarning(dcAlphaConnectModbusTcpConnection()) << "Modbus reply error occurred while updating \"Smart grid control\" registers from" << hostAddress().toString() << error << reply->errorString(); + emit reply->finished(); // To make sure it will be deleted + }); + } else { + delete reply; // Broadcast reply returns immediatly + } + } else { + qCWarning(dcAlphaConnectModbusTcpConnection()) << "Error occurred while reading \"Smart grid control\" registers from" << hostAddress().toString() << errorString(); + } +} + +QModbusReply *AlphaConnectModbusTcpConnection::readFlowTemperature() +{ + QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::InputRegisters, 1, 1); + return sendReadRequest(request, m_slaveId); +} + +QModbusReply *AlphaConnectModbusTcpConnection::readReturnTemperature() +{ + QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::InputRegisters, 2, 1); + return sendReadRequest(request, m_slaveId); +} + +QModbusReply *AlphaConnectModbusTcpConnection::readExternalReturnTemperature() +{ + QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::InputRegisters, 3, 1); + return sendReadRequest(request, m_slaveId); +} + +QModbusReply *AlphaConnectModbusTcpConnection::readHotWaterTemperature() +{ + QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::InputRegisters, 4, 1); + return sendReadRequest(request, m_slaveId); +} + +QModbusReply *AlphaConnectModbusTcpConnection::readHotGasTemperature() +{ + QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::InputRegisters, 8, 1); + return sendReadRequest(request, m_slaveId); +} + +QModbusReply *AlphaConnectModbusTcpConnection::readHeatSourceInletTemperature() +{ + QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::InputRegisters, 9, 1); + return sendReadRequest(request, m_slaveId); +} + +QModbusReply *AlphaConnectModbusTcpConnection::readHeatSourceOutletTemperature() +{ + QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::InputRegisters, 10, 1); + return sendReadRequest(request, m_slaveId); +} + +QModbusReply *AlphaConnectModbusTcpConnection::readRoomTemperature1() +{ + QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::InputRegisters, 11, 1); + return sendReadRequest(request, m_slaveId); +} + +QModbusReply *AlphaConnectModbusTcpConnection::readRoomTemperature2() +{ + QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::InputRegisters, 12, 1); + return sendReadRequest(request, m_slaveId); +} + +QModbusReply *AlphaConnectModbusTcpConnection::readRoomTemperature3() +{ + QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::InputRegisters, 13, 1); + return sendReadRequest(request, m_slaveId); +} + +QModbusReply *AlphaConnectModbusTcpConnection::readSolarCollectorTemperature() +{ + QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::InputRegisters, 14, 1); + return sendReadRequest(request, m_slaveId); +} + +QModbusReply *AlphaConnectModbusTcpConnection::readSolarStorageTankTemperature() +{ + QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::InputRegisters, 15, 1); + return sendReadRequest(request, m_slaveId); +} + +QModbusReply *AlphaConnectModbusTcpConnection::readExternalEnergySourceTemperature() +{ + QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::InputRegisters, 16, 1); + return sendReadRequest(request, m_slaveId); +} + +QModbusReply *AlphaConnectModbusTcpConnection::readSupplyAirTemperature() +{ + QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::InputRegisters, 17, 1); + return sendReadRequest(request, m_slaveId); +} + +QModbusReply *AlphaConnectModbusTcpConnection::readExternalAirTemperature() +{ + QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::InputRegisters, 18, 1); + return sendReadRequest(request, m_slaveId); +} + +QModbusReply *AlphaConnectModbusTcpConnection::readRbeRoomActualTemperature() +{ + QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::InputRegisters, 24, 1); + return sendReadRequest(request, m_slaveId); +} + +QModbusReply *AlphaConnectModbusTcpConnection::readRbeRoomSetpointTemperature() +{ + QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::InputRegisters, 24, 1); + return sendReadRequest(request, m_slaveId); +} + +QModbusReply *AlphaConnectModbusTcpConnection::readHeatingPumpOperatingHours() +{ + QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::InputRegisters, 33, 1); + return sendReadRequest(request, m_slaveId); +} + +QModbusReply *AlphaConnectModbusTcpConnection::readSystemStatus() +{ + QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::InputRegisters, 37, 1); + return sendReadRequest(request, m_slaveId); +} + +QModbusReply *AlphaConnectModbusTcpConnection::readHeatingEnergy() +{ + QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::InputRegisters, 38, 2); + return sendReadRequest(request, m_slaveId); +} + +QModbusReply *AlphaConnectModbusTcpConnection::readWaterHeatEnergy() +{ + QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::InputRegisters, 40, 2); + return sendReadRequest(request, m_slaveId); +} + +QModbusReply *AlphaConnectModbusTcpConnection::readTotalHeatEnergy() +{ + QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::InputRegisters, 44, 2); + return sendReadRequest(request, m_slaveId); +} + +QModbusReply *AlphaConnectModbusTcpConnection::readOutdoorTemperature() +{ + QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::HoldingRegisters, 0, 1); + return sendReadRequest(request, m_slaveId); +} + +QModbusReply *AlphaConnectModbusTcpConnection::readReturnSetpointTemperature() +{ + QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::HoldingRegisters, 1, 1); + return sendReadRequest(request, m_slaveId); +} + +QModbusReply *AlphaConnectModbusTcpConnection::readHotWaterSetpointTemperature() +{ + QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::HoldingRegisters, 5, 1); + return sendReadRequest(request, m_slaveId); +} + +QModbusReply *AlphaConnectModbusTcpConnection::readSmartGrid() +{ + QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::HoldingRegisters, 14, 1); + return sendReadRequest(request, m_slaveId); +} + +void AlphaConnectModbusTcpConnection::verifyInitFinished() +{ + if (m_pendingInitReplies.isEmpty()) { + qCDebug(dcAlphaConnectModbusTcpConnection()) << "Initialization finished of AlphaConnectModbusTcpConnection" << hostAddress().toString(); + emit initializationFinished(); + } +} + +QDebug operator<<(QDebug debug, AlphaConnectModbusTcpConnection *alphaConnectModbusTcpConnection) +{ + debug.nospace().noquote() << "AlphaConnectModbusTcpConnection(" << alphaConnectModbusTcpConnection->hostAddress().toString() << ":" << alphaConnectModbusTcpConnection->port() << ")" << "\n"; + debug.nospace().noquote() << " - Flow:" << alphaConnectModbusTcpConnection->flowTemperature() << " [°C]" << "\n"; + debug.nospace().noquote() << " - Return:" << alphaConnectModbusTcpConnection->returnTemperature() << " [°C]" << "\n"; + debug.nospace().noquote() << " - External return:" << alphaConnectModbusTcpConnection->externalReturnTemperature() << " [°C]" << "\n"; + debug.nospace().noquote() << " - Hot water temperature:" << alphaConnectModbusTcpConnection->hotWaterTemperature() << " [°C]" << "\n"; + debug.nospace().noquote() << " - Hot gas temperature:" << alphaConnectModbusTcpConnection->hotGasTemperature() << " [°C]" << "\n"; + debug.nospace().noquote() << " - Heat source inlet temperature:" << alphaConnectModbusTcpConnection->heatSourceInletTemperature() << " [°C]" << "\n"; + debug.nospace().noquote() << " - Heat source outlet temperature:" << alphaConnectModbusTcpConnection->heatSourceOutletTemperature() << " [°C]" << "\n"; + debug.nospace().noquote() << " - Room remote adjuster 1 temperature:" << alphaConnectModbusTcpConnection->roomTemperature1() << " [°C]" << "\n"; + debug.nospace().noquote() << " - Room remote adjuster 2 temperature:" << alphaConnectModbusTcpConnection->roomTemperature2() << " [°C]" << "\n"; + debug.nospace().noquote() << " - Room remote adjuster 3 temperature:" << alphaConnectModbusTcpConnection->roomTemperature3() << " [°C]" << "\n"; + debug.nospace().noquote() << " - Solar collector temperature:" << alphaConnectModbusTcpConnection->solarCollectorTemperature() << " [°C]" << "\n"; + debug.nospace().noquote() << " - Solar storage tank temperature:" << alphaConnectModbusTcpConnection->solarStorageTankTemperature() << " [°C]" << "\n"; + debug.nospace().noquote() << " - External energy source temperature:" << alphaConnectModbusTcpConnection->externalEnergySourceTemperature() << " [°C]" << "\n"; + debug.nospace().noquote() << " - Supply air temperature:" << alphaConnectModbusTcpConnection->supplyAirTemperature() << " [°C]" << "\n"; + debug.nospace().noquote() << " - External air temperature:" << alphaConnectModbusTcpConnection->externalAirTemperature() << " [°C]" << "\n"; + debug.nospace().noquote() << " - RBE actual room temperature:" << alphaConnectModbusTcpConnection->rbeRoomActualTemperature() << " [°C]" << "\n"; + debug.nospace().noquote() << " - RBE room temperature setpoint:" << alphaConnectModbusTcpConnection->rbeRoomSetpointTemperature() << " [°C]" << "\n"; + debug.nospace().noquote() << " - Heating pump operating hours:" << alphaConnectModbusTcpConnection->heatingPumpOperatingHours() << " [h]" << "\n"; + debug.nospace().noquote() << " - System status:" << alphaConnectModbusTcpConnection->systemStatus() << "\n"; + debug.nospace().noquote() << " - Heating energy:" << alphaConnectModbusTcpConnection->heatingEnergy() << " [kWh]" << "\n"; + debug.nospace().noquote() << " - Water heat energy:" << alphaConnectModbusTcpConnection->waterHeatEnergy() << " [kWh]" << "\n"; + debug.nospace().noquote() << " - Total energy:" << alphaConnectModbusTcpConnection->totalHeatEnergy() << " [kWh]" << "\n"; + debug.nospace().noquote() << " - Outdoor temperature:" << alphaConnectModbusTcpConnection->outdoorTemperature() << " [°C]" << "\n"; + debug.nospace().noquote() << " - Return setpoint temperature:" << alphaConnectModbusTcpConnection->returnSetpointTemperature() << " [°C]" << "\n"; + debug.nospace().noquote() << " - Hot water setpoint temperature:" << alphaConnectModbusTcpConnection->hotWaterSetpointTemperature() << " [°C]" << "\n"; + debug.nospace().noquote() << " - Smart grid control:" << alphaConnectModbusTcpConnection->smartGrid() << "\n"; + return debug.quote().space(); +} + diff --git a/alphainnotec/alphaconnectmodbustcpconnection.h b/alphainnotec/alphaconnectmodbustcpconnection.h new file mode 100644 index 0000000..4b27fd1 --- /dev/null +++ b/alphainnotec/alphaconnectmodbustcpconnection.h @@ -0,0 +1,273 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +* +* Copyright 2013 - 2021, nymea GmbH +* Contact: contact@nymea.io +* +* This fileDescriptor 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 . +* +* 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 ALPHACONNECTMODBUSTCPCONNECTION_H +#define ALPHACONNECTMODBUSTCPCONNECTION_H + +#include + +#include "../modbus/modbusdatautils.h" +#include "../modbus/modbustcpmaster.h" + +class AlphaConnectModbusTcpConnection : public ModbusTCPMaster +{ + Q_OBJECT +public: + enum SystemStatus { + SystemStatusHeatingMode = 0, + SystemStatusDomesticHotWater = 1, + SystemStatusSwimmingPool = 2, + SystemStatusEVUOff = 3, + SystemStatusDefrost = 4, + SystemStatusOff = 5, + SystemStatusExternalEnergySource = 6, + SystemStatusCoolingMode = 7 + }; + Q_ENUM(SystemStatus) + + enum SmartGridState { + SmartGridStateOff = 0, + SmartGridStateLow = 1, + SmartGridStateStandard = 2, + SmartGridStateHigh = 3 + }; + Q_ENUM(SmartGridState) + + explicit AlphaConnectModbusTcpConnection(const QHostAddress &hostAddress, uint port, quint16 slaveId, QObject *parent = nullptr); + ~AlphaConnectModbusTcpConnection() = default; + + /* Flow [°C] - Address: 1, Size: 1 */ + float flowTemperature() const; + + /* Return [°C] - Address: 2, Size: 1 */ + float returnTemperature() const; + + /* External return [°C] - Address: 3, Size: 1 */ + float externalReturnTemperature() const; + + /* Hot water temperature [°C] - Address: 4, Size: 1 */ + float hotWaterTemperature() const; + + /* Hot gas temperature [°C] - Address: 8, Size: 1 */ + float hotGasTemperature() const; + + /* Heat source inlet temperature [°C] - Address: 9, Size: 1 */ + float heatSourceInletTemperature() const; + + /* Heat source outlet temperature [°C] - Address: 10, Size: 1 */ + float heatSourceOutletTemperature() const; + + /* Room remote adjuster 1 temperature [°C] - Address: 11, Size: 1 */ + float roomTemperature1() const; + + /* Room remote adjuster 2 temperature [°C] - Address: 12, Size: 1 */ + float roomTemperature2() const; + + /* Room remote adjuster 3 temperature [°C] - Address: 13, Size: 1 */ + float roomTemperature3() const; + + /* Solar collector temperature [°C] - Address: 14, Size: 1 */ + float solarCollectorTemperature() const; + + /* Solar storage tank temperature [°C] - Address: 15, Size: 1 */ + float solarStorageTankTemperature() const; + + /* External energy source temperature [°C] - Address: 16, Size: 1 */ + float externalEnergySourceTemperature() const; + + /* Supply air temperature [°C] - Address: 17, Size: 1 */ + float supplyAirTemperature() const; + + /* External air temperature [°C] - Address: 18, Size: 1 */ + float externalAirTemperature() const; + + /* RBE actual room temperature [°C] - Address: 24, Size: 1 */ + float rbeRoomActualTemperature() const; + + /* RBE room temperature setpoint [°C] - Address: 24, Size: 1 */ + float rbeRoomSetpointTemperature() const; + + /* Heating pump operating hours [h] - Address: 33, Size: 1 */ + quint16 heatingPumpOperatingHours() const; + + /* System status - Address: 37, Size: 1 */ + SystemStatus systemStatus() const; + + /* Heating energy [kWh] - Address: 38, Size: 2 */ + float heatingEnergy() const; + + /* Water heat energy [kWh] - Address: 40, Size: 2 */ + float waterHeatEnergy() const; + + /* Total energy [kWh] - Address: 44, Size: 2 */ + float totalHeatEnergy() const; + + /* Outdoor temperature [°C] - Address: 0, Size: 1 */ + float outdoorTemperature() const; + QModbusReply *setOutdoorTemperature(float outdoorTemperature); + + /* Return setpoint temperature [°C] - Address: 1, Size: 1 */ + float returnSetpointTemperature() const; + QModbusReply *setReturnSetpointTemperature(float returnSetpointTemperature); + + /* Hot water setpoint temperature [°C] - Address: 5, Size: 1 */ + float hotWaterSetpointTemperature() const; + QModbusReply *setHotWaterSetpointTemperature(float hotWaterSetpointTemperature); + + /* Smart grid control - Address: 14, Size: 1 */ + SmartGridState smartGrid() const; + QModbusReply *setSmartGrid(SmartGridState smartGrid); + + virtual void initialize(); + virtual void update(); + + void updateFlowTemperature(); + void updateReturnTemperature(); + void updateExternalReturnTemperature(); + void updateHotWaterTemperature(); + void updateHotGasTemperature(); + void updateHeatSourceInletTemperature(); + void updateHeatSourceOutletTemperature(); + void updateRoomTemperature1(); + void updateRoomTemperature2(); + void updateRoomTemperature3(); + void updateSolarCollectorTemperature(); + void updateSolarStorageTankTemperature(); + void updateExternalEnergySourceTemperature(); + void updateSupplyAirTemperature(); + void updateExternalAirTemperature(); + void updateRbeRoomActualTemperature(); + void updateRbeRoomSetpointTemperature(); + void updateHeatingPumpOperatingHours(); + void updateSystemStatus(); + void updateHeatingEnergy(); + void updateWaterHeatEnergy(); + void updateTotalHeatEnergy(); + void updateOutdoorTemperature(); + void updateReturnSetpointTemperature(); + void updateHotWaterSetpointTemperature(); + void updateSmartGrid(); + +signals: + void initializationFinished(); + + void flowTemperatureChanged(float flowTemperature); + void returnTemperatureChanged(float returnTemperature); + void externalReturnTemperatureChanged(float externalReturnTemperature); + void hotWaterTemperatureChanged(float hotWaterTemperature); + void hotGasTemperatureChanged(float hotGasTemperature); + void heatSourceInletTemperatureChanged(float heatSourceInletTemperature); + void heatSourceOutletTemperatureChanged(float heatSourceOutletTemperature); + void roomTemperature1Changed(float roomTemperature1); + void roomTemperature2Changed(float roomTemperature2); + void roomTemperature3Changed(float roomTemperature3); + void solarCollectorTemperatureChanged(float solarCollectorTemperature); + void solarStorageTankTemperatureChanged(float solarStorageTankTemperature); + void externalEnergySourceTemperatureChanged(float externalEnergySourceTemperature); + void supplyAirTemperatureChanged(float supplyAirTemperature); + void externalAirTemperatureChanged(float externalAirTemperature); + void rbeRoomActualTemperatureChanged(float rbeRoomActualTemperature); + void rbeRoomSetpointTemperatureChanged(float rbeRoomSetpointTemperature); + void heatingPumpOperatingHoursChanged(quint16 heatingPumpOperatingHours); + void systemStatusChanged(SystemStatus systemStatus); + void heatingEnergyChanged(float heatingEnergy); + void waterHeatEnergyChanged(float waterHeatEnergy); + void totalHeatEnergyChanged(float totalHeatEnergy); + void outdoorTemperatureChanged(float outdoorTemperature); + void returnSetpointTemperatureChanged(float returnSetpointTemperature); + void hotWaterSetpointTemperatureChanged(float hotWaterSetpointTemperature); + void smartGridChanged(SmartGridState smartGrid); + +private: + quint16 m_slaveId = 1; + QVector m_pendingInitReplies; + + float m_flowTemperature = 0; + float m_returnTemperature = 0; + float m_externalReturnTemperature = 0; + float m_hotWaterTemperature = 0; + float m_hotGasTemperature = 0; + float m_heatSourceInletTemperature = 0; + float m_heatSourceOutletTemperature = 0; + float m_roomTemperature1 = 0; + float m_roomTemperature2 = 0; + float m_roomTemperature3 = 0; + float m_solarCollectorTemperature = 0; + float m_solarStorageTankTemperature = 0; + float m_externalEnergySourceTemperature = 0; + float m_supplyAirTemperature = 0; + float m_externalAirTemperature = 0; + float m_rbeRoomActualTemperature = 0; + float m_rbeRoomSetpointTemperature = 0; + quint16 m_heatingPumpOperatingHours = 0; + SystemStatus m_systemStatus = SystemStatusHeatingMode; + float m_heatingEnergy = 0; + float m_waterHeatEnergy = 0; + float m_totalHeatEnergy = 0; + float m_outdoorTemperature = 0; + float m_returnSetpointTemperature = 0; + float m_hotWaterSetpointTemperature = 0; + SmartGridState m_smartGrid = SmartGridStateStandard; + + void verifyInitFinished(); + + QModbusReply *readFlowTemperature(); + QModbusReply *readReturnTemperature(); + QModbusReply *readExternalReturnTemperature(); + QModbusReply *readHotWaterTemperature(); + QModbusReply *readHotGasTemperature(); + QModbusReply *readHeatSourceInletTemperature(); + QModbusReply *readHeatSourceOutletTemperature(); + QModbusReply *readRoomTemperature1(); + QModbusReply *readRoomTemperature2(); + QModbusReply *readRoomTemperature3(); + QModbusReply *readSolarCollectorTemperature(); + QModbusReply *readSolarStorageTankTemperature(); + QModbusReply *readExternalEnergySourceTemperature(); + QModbusReply *readSupplyAirTemperature(); + QModbusReply *readExternalAirTemperature(); + QModbusReply *readRbeRoomActualTemperature(); + QModbusReply *readRbeRoomSetpointTemperature(); + QModbusReply *readHeatingPumpOperatingHours(); + QModbusReply *readSystemStatus(); + QModbusReply *readHeatingEnergy(); + QModbusReply *readWaterHeatEnergy(); + QModbusReply *readTotalHeatEnergy(); + QModbusReply *readOutdoorTemperature(); + QModbusReply *readReturnSetpointTemperature(); + QModbusReply *readHotWaterSetpointTemperature(); + QModbusReply *readSmartGrid(); + + +}; + +QDebug operator<<(QDebug debug, AlphaConnectModbusTcpConnection *alphaConnectModbusTcpConnection); + +#endif // ALPHACONNECTMODBUSTCPCONNECTION_H diff --git a/alphainnotec/alphainnotec-registers.json b/alphainnotec/alphainnotec-registers.json new file mode 100644 index 0000000..1908e0d --- /dev/null +++ b/alphainnotec/alphainnotec-registers.json @@ -0,0 +1,401 @@ +{ + "endianness": "BigEndian", + "enums": [ + { + "name": "SystemStatus", + "values": [ + { + "key": "HeatingMode", + "value": 0 + }, + { + "key": "DomesticHotWater", + "value": 1 + }, + { + "key": "SwimmingPool", + "value": 2 + }, + { + "key": "EVUOff", + "value": 3 + }, + { + "key": "Defrost", + "value": 4 + }, + { + "key": "Off", + "value": 5 + }, + { + "key": "ExternalEnergySource", + "value": 6 + }, + { + "key": "CoolingMode", + "value": 7 + } + ] + }, + { + "name": "SmartGridState", + "values": [ + { + "key": "Off", + "value": 0 + }, + { + "key": "Low", + "value": 1 + }, + { + "key": "Standard", + "value": 2 + }, + { + "key": "High", + "value": 3 + } + ] + } + ], + "registers": [ + { + "id": "flowTemperature", + "address": 1, + "size": 1, + "type": "uint16", + "registerType": "inputRegister", + "readSchedule": "update", + "description": "Flow", + "staticScaleFactor": -1, + "defaultValue": "0", + "unit": "°C", + "access": "RO" + }, + { + "id": "returnTemperature", + "address": 2, + "size": 1, + "type": "uint16", + "registerType": "inputRegister", + "readSchedule": "update", + "description": "Return", + "staticScaleFactor": -1, + "defaultValue": "0", + "unit": "°C", + "access": "RO" + }, + { + "id": "externalReturnTemperature", + "address": 3, + "size": 1, + "type": "uint16", + "registerType": "inputRegister", + "readSchedule": "update", + "description": "External return", + "staticScaleFactor": -1, + "defaultValue": "0", + "unit": "°C", + "access": "RO" + }, + { + "id": "hotWaterTemperature", + "address": 4, + "size": 1, + "type": "uint16", + "registerType": "inputRegister", + "readSchedule": "update", + "description": "Hot water temperature", + "staticScaleFactor": -1, + "defaultValue": "0", + "unit": "°C", + "access": "RO" + }, + { + "id": "hotGasTemperature", + "address": 8, + "size": 1, + "type": "uint16", + "registerType": "inputRegister", + "readSchedule": "update", + "description": "Hot gas temperature", + "staticScaleFactor": -1, + "defaultValue": "0", + "unit": "°C", + "access": "RO" + }, + { + "id": "heatSourceInletTemperature", + "address": 9, + "size": 1, + "type": "uint16", + "registerType": "inputRegister", + "readSchedule": "update", + "description": "Heat source inlet temperature", + "staticScaleFactor": -1, + "defaultValue": "0", + "unit": "°C", + "access": "RO" + }, + { + "id": "heatSourceOutletTemperature", + "address": 10, + "size": 1, + "type": "uint16", + "registerType": "inputRegister", + "readSchedule": "update", + "description": "Heat source outlet temperature", + "staticScaleFactor": -1, + "defaultValue": "0", + "unit": "°C", + "access": "RO" + }, + { + "id": "roomTemperature1", + "address": 11, + "size": 1, + "type": "uint16", + "registerType": "inputRegister", + "readSchedule": "update", + "description": "Room remote adjuster 1 temperature", + "staticScaleFactor": -1, + "defaultValue": "0", + "unit": "°C", + "access": "RO" + }, + { + "id": "roomTemperature2", + "address": 12, + "size": 1, + "type": "uint16", + "registerType": "inputRegister", + "readSchedule": "update", + "description": "Room remote adjuster 2 temperature", + "staticScaleFactor": -1, + "defaultValue": "0", + "unit": "°C", + "access": "RO" + }, + { + "id": "roomTemperature3", + "address": 13, + "size": 1, + "type": "uint16", + "registerType": "inputRegister", + "readSchedule": "update", + "description": "Room remote adjuster 3 temperature", + "staticScaleFactor": -1, + "defaultValue": "0", + "unit": "°C", + "access": "RO" + }, + { + "id": "solarCollectorTemperature", + "address": 14, + "size": 1, + "type": "uint16", + "registerType": "inputRegister", + "readSchedule": "update", + "description": "Solar collector temperature", + "staticScaleFactor": -1, + "defaultValue": "0", + "unit": "°C", + "access": "RO" + }, + { + "id": "solarStorageTankTemperature", + "address": 15, + "size": 1, + "type": "uint16", + "registerType": "inputRegister", + "readSchedule": "update", + "description": "Solar storage tank temperature", + "staticScaleFactor": -1, + "defaultValue": "0", + "unit": "°C", + "access": "RO" + }, + { + "id": "externalEnergySourceTemperature", + "address": 16, + "size": 1, + "type": "uint16", + "registerType": "inputRegister", + "readSchedule": "update", + "description": "External energy source temperature", + "staticScaleFactor": -1, + "defaultValue": "0", + "unit": "°C", + "access": "RO" + }, + { + "id": "supplyAirTemperature", + "address": 17, + "size": 1, + "type": "uint16", + "registerType": "inputRegister", + "readSchedule": "update", + "description": "Supply air temperature", + "staticScaleFactor": -1, + "defaultValue": "0", + "unit": "°C", + "access": "RO" + }, + { + "id": "externalAirTemperature", + "address": 18, + "size": 1, + "type": "uint16", + "registerType": "inputRegister", + "readSchedule": "update", + "description": "External air temperature", + "staticScaleFactor": -1, + "defaultValue": "0", + "unit": "°C", + "access": "RO" + }, + { + "id": "rbeRoomActualTemperature", + "address": 24, + "size": 1, + "type": "uint16", + "registerType": "inputRegister", + "readSchedule": "update", + "description": "RBE actual room temperature", + "staticScaleFactor": -1, + "defaultValue": "0", + "unit": "°C", + "access": "RO" + }, + { + "id": "rbeRoomSetpointTemperature", + "address": 24, + "size": 1, + "type": "uint16", + "registerType": "inputRegister", + "readSchedule": "update", + "description": "RBE room temperature setpoint", + "staticScaleFactor": -1, + "defaultValue": "0", + "unit": "°C", + "access": "RO" + }, + { + "id": "heatingPumpOperatingHours", + "address": 33, + "size": 1, + "type": "uint16", + "registerType": "inputRegister", + "readSchedule": "update", + "description": "Heating pump operating hours", + "defaultValue": "0", + "unit": "h", + "access": "RO" + }, + { + "id": "systemStatus", + "address": 37, + "size": 1, + "type": "uint16", + "enum": "SystemStatus", + "registerType": "inputRegister", + "readSchedule": "update", + "description": "System status", + "defaultValue": "SystemStatusHeatingMode", + "access": "RO" + }, + { + "id": "heatingEnergy", + "address": 38, + "size": 2, + "type": "uint32", + "registerType": "inputRegister", + "readSchedule": "update", + "staticScaleFactor": -1, + "defaultValue": "0", + "unit": "kWh", + "description": "Heating energy", + "access": "RO" + }, + { + "id": "waterHeatEnergy", + "address": 40, + "size": 2, + "type": "uint32", + "registerType": "inputRegister", + "readSchedule": "update", + "staticScaleFactor": -1, + "defaultValue": "0", + "unit": "kWh", + "description": "Water heat energy", + "access": "RO" + }, + { + "id": "totalHeatEnergy", + "address": 44, + "size": 2, + "type": "uint32", + "registerType": "inputRegister", + "readSchedule": "update", + "staticScaleFactor": -1, + "defaultValue": "0", + "unit": "kWh", + "description": "Total energy", + "access": "RO" + }, + + { + "id": "outdoorTemperature", + "address": 0, + "size": 1, + "type": "uint16", + "registerType": "holdingRegister", + "readSchedule": "update", + "description": "Outdoor temperature", + "staticScaleFactor": -1, + "defaultValue": "0", + "unit": "°C", + "access": "RW" + }, + { + "id": "returnSetpointTemperature", + "address": 1, + "size": 1, + "type": "uint16", + "registerType": "holdingRegister", + "readSchedule": "update", + "description": "Return setpoint temperature", + "staticScaleFactor": -1, + "defaultValue": "0", + "unit": "°C", + "access": "RW" + }, + { + "id": "hotWaterSetpointTemperature", + "address": 5, + "size": 1, + "type": "uint16", + "registerType": "holdingRegister", + "readSchedule": "update", + "description": "Hot water setpoint temperature", + "staticScaleFactor": -1, + "defaultValue": "0", + "unit": "°C", + "access": "RW" + }, + { + "id": "smartGrid", + "address": 14, + "size": 1, + "type": "uint16", + "enum": "SmartGridState", + "registerType": "holdingRegister", + "readSchedule": "update", + "description": "Smart grid control", + "defaultValue": "SmartGridStateStandard", + "access": "RW" + } + ] +} diff --git a/alphainnotec/alphainnotec.pro b/alphainnotec/alphainnotec.pro new file mode 100644 index 0000000..b88c856 --- /dev/null +++ b/alphainnotec/alphainnotec.pro @@ -0,0 +1,15 @@ +include(../plugins.pri) + +QT += network serialbus + +SOURCES += \ + integrationpluginalphainnotec.cpp \ + alphaconnectmodbustcpconnection.cpp \ + ../modbus/modbustcpmaster.cpp \ + ../modbus/modbusdatautils.cpp + +HEADERS += \ + integrationpluginalphainnotec.h \ + alphaconnectmodbustcpconnection.h \ + ../modbus/modbustcpmaster.h \ + ../modbus/modbusdatautils.h diff --git a/alphainnotec/integrationpluginalphainnotec.cpp b/alphainnotec/integrationpluginalphainnotec.cpp new file mode 100644 index 0000000..153d6b3 --- /dev/null +++ b/alphainnotec/integrationpluginalphainnotec.cpp @@ -0,0 +1,474 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +* +* Copyright 2013 - 2021, 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 . +* +* 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 "integrationpluginalphainnotec.h" + +#include "network/networkdevicediscovery.h" +#include "hardwaremanager.h" +#include "plugininfo.h" + +IntegrationPluginAlphaInnotec::IntegrationPluginAlphaInnotec() +{ + +} + +void IntegrationPluginAlphaInnotec::discoverThings(ThingDiscoveryInfo *info) +{ + if (!hardwareManager()->networkDeviceDiscovery()->available()) { + qCWarning(dcAlphaInnotec()) << "The network discovery is not available on this platform."; + info->finish(Thing::ThingErrorUnsupportedFeature, QT_TR_NOOP("The network device discovery is not available.")); + return; + } + + NetworkDeviceDiscoveryReply *discoveryReply = hardwareManager()->networkDeviceDiscovery()->discover(); + connect(discoveryReply, &NetworkDeviceDiscoveryReply::finished, this, [=](){ + foreach (const NetworkDeviceInfo &networkDeviceInfo, discoveryReply->networkDeviceInfos()) { + + qCDebug(dcAlphaInnotec()) << "Found" << networkDeviceInfo; + + QString title; + if (networkDeviceInfo.hostName().isEmpty()) { + title = networkDeviceInfo.address().toString(); + } else { + title = networkDeviceInfo.hostName() + " (" + networkDeviceInfo.address().toString() + ")"; + } + + QString description; + if (networkDeviceInfo.macAddressManufacturer().isEmpty()) { + description = networkDeviceInfo.macAddress(); + } else { + description = networkDeviceInfo.macAddress() + " (" + networkDeviceInfo.macAddressManufacturer() + ")"; + } + + ThingDescriptor descriptor(alphaConnectThingClassId, title, description); + ParamList params; + params << Param(alphaConnectThingIpAddressParamTypeId, networkDeviceInfo.address().toString()); + params << Param(alphaConnectThingMacAddressParamTypeId, networkDeviceInfo.macAddress()); + descriptor.setParams(params); + + // Check if we already have set up this device + Things existingThings = myThings().filterByParam(alphaConnectThingMacAddressParamTypeId, networkDeviceInfo.macAddress()); + if (existingThings.count() == 1) { + qCDebug(dcAlphaInnotec()) << "This connection already exists in the system:" << networkDeviceInfo; + descriptor.setThingId(existingThings.first()->id()); + } + + info->addThingDescriptor(descriptor); + } + + info->finish(Thing::ThingErrorNoError); + }); +} + +void IntegrationPluginAlphaInnotec::startMonitoringAutoThings() +{ + +} + +void IntegrationPluginAlphaInnotec::setupThing(ThingSetupInfo *info) +{ + Thing *thing = info->thing(); + qCDebug(dcAlphaInnotec()) << "Setup" << thing << thing->params(); + + if (thing->thingClassId() == alphaConnectThingClassId) { + QHostAddress hostAddress = QHostAddress(thing->paramValue(alphaConnectThingIpAddressParamTypeId).toString()); + if (hostAddress.isNull()) { + info->finish(Thing::ThingErrorInvalidParameter, QT_TR_NOOP("No IP address given")); + return; + } + + uint port = thing->paramValue(alphaConnectThingPortParamTypeId).toUInt(); + quint16 slaveId = thing->paramValue(alphaConnectThingSlaveIdParamTypeId).toUInt(); + + AlphaConnectModbusTcpConnection *alphaConnectTcpConnection = new AlphaConnectModbusTcpConnection(hostAddress, port, slaveId, this); + connect(alphaConnectTcpConnection, &AlphaConnectModbusTcpConnection::connectionStateChanged, this, [thing, alphaConnectTcpConnection](bool status){ + qCDebug(dcAlphaInnotec()) << "Connected changed to" << status << "for" << thing; + if (status) { + alphaConnectTcpConnection->update(); + } + + thing->setStateValue(alphaConnectConnectedStateTypeId, status); + }); + + + // Input registers +// connect(alphaConnectTcpConnection, &AlphaConnectModbusTcpConnection::meanTemperatureChanged, this, [thing](float meanTemperature){ +// qCDebug(dcAlphaInnotec()) << thing << "mean temperature changed" << meanTemperature << "°C"; +// thing->setStateValue(alphaConnectMeanTemperatureStateTypeId, meanTemperature); +// }); + + connect(alphaConnectTcpConnection, &AlphaConnectModbusTcpConnection::flowTemperatureChanged, this, [thing](float flowTemperature){ + qCDebug(dcAlphaInnotec()) << thing << "flow temperature changed" << flowTemperature << "°C"; + thing->setStateValue(alphaConnectFlowTemperatureStateTypeId, flowTemperature); + }); + + connect(alphaConnectTcpConnection, &AlphaConnectModbusTcpConnection::returnTemperatureChanged, this, [thing](float returnTemperature){ + qCDebug(dcAlphaInnotec()) << thing << "return temperature changed" << returnTemperature << "°C"; + thing->setStateValue(alphaConnectReturnTemperatureStateTypeId, returnTemperature); + }); + + connect(alphaConnectTcpConnection, &AlphaConnectModbusTcpConnection::externalReturnTemperatureChanged, this, [thing](float externalReturnTemperature){ + qCDebug(dcAlphaInnotec()) << thing << "external return temperature changed" << externalReturnTemperature << "°C"; + thing->setStateValue(alphaConnectExternalReturnTemperatureStateTypeId, externalReturnTemperature); + }); + + connect(alphaConnectTcpConnection, &AlphaConnectModbusTcpConnection::hotWaterTemperatureChanged, this, [thing](float hotWaterTemperature){ + qCDebug(dcAlphaInnotec()) << thing << "hot water temperature changed" << hotWaterTemperature << "°C"; + thing->setStateValue(alphaConnectHotWaterTemperatureStateTypeId, hotWaterTemperature); + }); + + connect(alphaConnectTcpConnection, &AlphaConnectModbusTcpConnection::hotGasTemperatureChanged, this, [thing](float hotGasTemperature){ + qCDebug(dcAlphaInnotec()) << thing << "hot gas temperature changed" << hotGasTemperature << "°C"; + thing->setStateValue(alphaConnectHotGasTemperatureStateTypeId, hotGasTemperature); + }); + + connect(alphaConnectTcpConnection, &AlphaConnectModbusTcpConnection::heatSourceInletTemperatureChanged, this, [thing](float heatSourceInletTemperature){ + qCDebug(dcAlphaInnotec()) << thing << "heat source inlet temperature changed" << heatSourceInletTemperature << "°C"; + thing->setStateValue(alphaConnectHeatSourceInletTemperatureStateTypeId, heatSourceInletTemperature); + }); + + connect(alphaConnectTcpConnection, &AlphaConnectModbusTcpConnection::heatSourceOutletTemperatureChanged, this, [thing](float heatSourceOutletTemperature){ + qCDebug(dcAlphaInnotec()) << thing << "heat source outlet temperature changed" << heatSourceOutletTemperature << "°C"; + thing->setStateValue(alphaConnectHeatSourceOutletTemperatureStateTypeId, heatSourceOutletTemperature); + }); + + connect(alphaConnectTcpConnection, &AlphaConnectModbusTcpConnection::roomTemperature1Changed, this, [thing](float roomTemperature1){ + qCDebug(dcAlphaInnotec()) << thing << "room remote adjuster 1 temperature changed" << roomTemperature1 << "°C"; + thing->setStateValue(alphaConnectRoomTemperature1StateTypeId, roomTemperature1); + }); + + connect(alphaConnectTcpConnection, &AlphaConnectModbusTcpConnection::roomTemperature2Changed, this, [thing](float roomTemperature2){ + qCDebug(dcAlphaInnotec()) << thing << "room remote adjuster 2 temperature changed" << roomTemperature2 << "°C"; + thing->setStateValue(alphaConnectRoomTemperature2StateTypeId, roomTemperature2); + }); + + connect(alphaConnectTcpConnection, &AlphaConnectModbusTcpConnection::roomTemperature3Changed, this, [thing](float roomTemperature3){ + qCDebug(dcAlphaInnotec()) << thing << "room remote adjuster 3 temperature changed" << roomTemperature3 << "°C"; + thing->setStateValue(alphaConnectRoomTemperature2StateTypeId, roomTemperature3); + }); + + connect(alphaConnectTcpConnection, &AlphaConnectModbusTcpConnection::solarCollectorTemperatureChanged, this, [thing](float solarCollectorTemperature){ + qCDebug(dcAlphaInnotec()) << thing << "solar collector temperature changed" << solarCollectorTemperature << "°C"; + thing->setStateValue(alphaConnectSolarCollectorTemperatureStateTypeId, solarCollectorTemperature); + }); + + connect(alphaConnectTcpConnection, &AlphaConnectModbusTcpConnection::solarStorageTankTemperatureChanged, this, [thing](float solarStorageTankTemperature){ + qCDebug(dcAlphaInnotec()) << thing << "solar storage tank temperature changed" << solarStorageTankTemperature << "°C"; + thing->setStateValue(alphaConnectSolarCollectorTemperatureStateTypeId, solarStorageTankTemperature); + }); + + connect(alphaConnectTcpConnection, &AlphaConnectModbusTcpConnection::externalEnergySourceTemperatureChanged, this, [thing](float externalEnergySourceTemperature){ + qCDebug(dcAlphaInnotec()) << thing << "external energy source temperature changed" << externalEnergySourceTemperature << "°C"; + thing->setStateValue(alphaConnectExternalEnergySourceTemperatureStateTypeId, externalEnergySourceTemperature); + }); + + connect(alphaConnectTcpConnection, &AlphaConnectModbusTcpConnection::supplyAirTemperatureChanged, this, [thing](float supplyAirTemperature){ + qCDebug(dcAlphaInnotec()) << thing << "supply air temperature changed" << supplyAirTemperature << "°C"; + thing->setStateValue(alphaConnectSupplyAirTemperatureStateTypeId, supplyAirTemperature); + }); + + connect(alphaConnectTcpConnection, &AlphaConnectModbusTcpConnection::externalAirTemperatureChanged, this, [thing](float externalAirTemperature){ + qCDebug(dcAlphaInnotec()) << thing << "external air temperature changed" << externalAirTemperature << "°C"; + thing->setStateValue(alphaConnectExternalAirTemperatureStateTypeId, externalAirTemperature); + }); + + connect(alphaConnectTcpConnection, &AlphaConnectModbusTcpConnection::heatingPumpOperatingHoursChanged, this, [thing](quint16 heatingPumpOperatingHours){ + qCDebug(dcAlphaInnotec()) << thing << "heating pump operating hours changed" << heatingPumpOperatingHours; + thing->setStateValue(alphaConnectHeatingPumpOperatingHoursStateTypeId, heatingPumpOperatingHours); + }); + + connect(alphaConnectTcpConnection, &AlphaConnectModbusTcpConnection::systemStatusChanged, this, [thing](AlphaConnectModbusTcpConnection::SystemStatus systemStatus){ + qCDebug(dcAlphaInnotec()) << thing << "system status changed" << systemStatus; + switch (systemStatus) { + case AlphaConnectModbusTcpConnection::SystemStatusHeatingMode: + thing->setStateValue(alphaConnectSystemStatusStateTypeId, "Heating mode"); + break; + case AlphaConnectModbusTcpConnection::SystemStatusDomesticHotWater: + thing->setStateValue(alphaConnectSystemStatusStateTypeId, "Domestic hot water"); + break; + case AlphaConnectModbusTcpConnection::SystemStatusSwimmingPool: + thing->setStateValue(alphaConnectSystemStatusStateTypeId, "Swimming pool"); + break; + case AlphaConnectModbusTcpConnection::SystemStatusEVUOff: + thing->setStateValue(alphaConnectSystemStatusStateTypeId, "EUV off"); + break; + case AlphaConnectModbusTcpConnection::SystemStatusDefrost: + thing->setStateValue(alphaConnectSystemStatusStateTypeId, "Defrost"); + break; + case AlphaConnectModbusTcpConnection::SystemStatusOff: + thing->setStateValue(alphaConnectSystemStatusStateTypeId, "Off"); + break; + case AlphaConnectModbusTcpConnection::SystemStatusExternalEnergySource: + thing->setStateValue(alphaConnectSystemStatusStateTypeId, "External energy source"); + break; + case AlphaConnectModbusTcpConnection::SystemStatusCoolingMode: + thing->setStateValue(alphaConnectSystemStatusStateTypeId, "Cooling mode"); + break; + } + + // Set heating and cooling states according to the system state + thing->setStateValue(alphaConnectHeatingOnStateTypeId, systemStatus == AlphaConnectModbusTcpConnection::SystemStatusHeatingMode); + thing->setStateValue(alphaConnectCoolingOnStateTypeId, systemStatus == AlphaConnectModbusTcpConnection::SystemStatusCoolingMode); + }); + + // Energy + connect(alphaConnectTcpConnection, &AlphaConnectModbusTcpConnection::totalHeatEnergyChanged, this, [thing](float totalHeatEnergy){ + qCDebug(dcAlphaInnotec()) << thing << "total heating energy changed" << totalHeatEnergy << "kWh"; + thing->setStateValue(alphaConnectTotalEnergyStateTypeId, totalHeatEnergy); + }); + + connect(alphaConnectTcpConnection, &AlphaConnectModbusTcpConnection::heatingEnergyChanged, this, [thing](float heatingEnergy){ + qCDebug(dcAlphaInnotec()) << thing << "heating energy changed" << heatingEnergy << "kWh"; + thing->setStateValue(alphaConnectHeatingEnergyStateTypeId, heatingEnergy); + }); + + connect(alphaConnectTcpConnection, &AlphaConnectModbusTcpConnection::waterHeatEnergyChanged, this, [thing](float waterHeatEnergy){ + qCDebug(dcAlphaInnotec()) << thing << "water heat energy changed" << waterHeatEnergy << "kWh"; + thing->setStateValue(alphaConnectHotWaterEnergyStateTypeId, waterHeatEnergy); + }); + +// connect(alphaConnectTcpConnection, &AlphaConnectModbusTcpConnection::swimmingPoolHeatEnergyChanged, this, [thing](float swimmingPoolHeatEnergy){ +// qCDebug(dcAlphaInnotec()) << thing << "swimming pool heat energy changed" << swimmingPoolHeatEnergy << "kWh"; +// thing->setStateValue(alphaConnectSwimmingPoolEnergyStateTypeId, swimmingPoolHeatEnergy); +// }); + + // Holding registers + connect(alphaConnectTcpConnection, &AlphaConnectModbusTcpConnection::outdoorTemperatureChanged, this, [thing](float outdoorTemperature){ + qCDebug(dcAlphaInnotec()) << thing << "outdoor temperature changed" << outdoorTemperature << "°C"; + thing->setStateValue(alphaConnectOutdoorTemperatureStateTypeId, outdoorTemperature); + }); + + connect(alphaConnectTcpConnection, &AlphaConnectModbusTcpConnection::returnSetpointTemperatureChanged, this, [thing](float returnSetpointTemperature){ + qCDebug(dcAlphaInnotec()) << thing << "return setpoint temperature changed" << returnSetpointTemperature << "°C"; + thing->setStateValue(alphaConnectReturnSetpointTemperatureStateTypeId, returnSetpointTemperature); + }); + + connect(alphaConnectTcpConnection, &AlphaConnectModbusTcpConnection::hotWaterSetpointTemperatureChanged, this, [thing](float hotWaterSetpointTemperature){ + qCDebug(dcAlphaInnotec()) << thing << "hot water setpoint temperature changed" << hotWaterSetpointTemperature << "°C"; + thing->setStateValue(alphaConnectHotWaterSetpointTemperatureStateTypeId, hotWaterSetpointTemperature); + }); + + connect(alphaConnectTcpConnection, &AlphaConnectModbusTcpConnection::smartGridChanged, this, [thing](AlphaConnectModbusTcpConnection::SmartGridState smartGridState){ + qCDebug(dcAlphaInnotec()) << thing << "smart grid state changed" << smartGridState; + switch (smartGridState) { + case AlphaConnectModbusTcpConnection::SmartGridStateOff: + thing->setStateValue(alphaConnectSgReadyModeStateTypeId, "Off"); + break; + case AlphaConnectModbusTcpConnection::SmartGridStateLow: + thing->setStateValue(alphaConnectSgReadyModeStateTypeId, "Low"); + break; + case AlphaConnectModbusTcpConnection::SmartGridStateStandard: + thing->setStateValue(alphaConnectSgReadyModeStateTypeId, "Standard"); + break; + case AlphaConnectModbusTcpConnection::SmartGridStateHigh: + thing->setStateValue(alphaConnectSgReadyModeStateTypeId, "High"); + break; + } + }); + + m_alpaConnectTcpThings.insert(thing, alphaConnectTcpConnection); + alphaConnectTcpConnection->connectDevice(); + + // FIXME: make async and check if this is really an alpha connect + info->finish(Thing::ThingErrorNoError); + } +} + +void IntegrationPluginAlphaInnotec::postSetupThing(Thing *thing) +{ + if (thing->thingClassId() == alphaConnectThingClassId) { + if (!m_pluginTimer) { + qCDebug(dcAlphaInnotec()) << "Starting plugin timer..."; + m_pluginTimer = hardwareManager()->pluginTimerManager()->registerTimer(10); + connect(m_pluginTimer, &PluginTimer::timeout, this, [this] { + foreach (AlphaConnectModbusTcpConnection *connection, m_alpaConnectTcpThings) { + if (connection->connected()) { + connection->update(); + } + } + }); + + m_pluginTimer->start(); + } + } +} + +void IntegrationPluginAlphaInnotec::thingRemoved(Thing *thing) +{ + if (thing->thingClassId() == alphaConnectThingClassId && m_alpaConnectTcpThings.contains(thing)) { + AlphaConnectModbusTcpConnection *connection = m_alpaConnectTcpThings.take(thing); + delete connection; + } + + if (myThings().isEmpty() && m_pluginTimer) { + hardwareManager()->pluginTimerManager()->unregisterTimer(m_pluginTimer); + m_pluginTimer = nullptr; + } +} + +void IntegrationPluginAlphaInnotec::executeAction(ThingActionInfo *info) +{ + Thing *thing = info->thing(); + AlphaConnectModbusTcpConnection *connection = m_alpaConnectTcpThings.value(thing); + + if (!connection->connected()) { + qCWarning(dcAlphaInnotec()) << "Could not execute action. The modbus connection is currently not available."; + info->finish(Thing::ThingErrorHardwareNotAvailable); + return; + } + + if (thing->thingClassId() == alphaConnectThingClassId) { + /* if (info->action().actionTypeId() == alphaConnectOutdoorTemperatureActionTypeId) { + double outdoorTemperature = info->action().paramValue(alphaConnectOutdoorTemperatureActionOutdoorTemperatureParamTypeId).toDouble(); + qCDebug(dcAlphaInnotec()) << "Execute action" << info->action().actionTypeId().toString() << info->action().params(); + QModbusReply *reply = connection->setOutdoorTemperature(outdoorTemperature); + if (!reply) { + qCWarning(dcAlphaInnotec()) << "Execute action failed because the reply could not be created."; + info->finish(Thing::ThingErrorHardwareFailure); + return; + } + + connect(reply, &QModbusReply::finished, reply, &QModbusReply::deleteLater); + connect(reply, &QModbusReply::finished, info, [info, reply, outdoorTemperature]{ + if (reply->error() != QModbusDevice::NoError) { + info->finish(Thing::ThingErrorHardwareFailure); + qCWarning(dcAlphaInnotec()) << "Set outdoor temperature finished with error" << reply->errorString(); + return; + } + + qCDebug(dcAlphaInnotec()) << "Execute action finished successfully" << info->action().actionTypeId().toString() << info->action().params(); + info->thing()->setStateValue(alphaConnectOutdoorTemperatureStateTypeId, outdoorTemperature); + info->finish(Thing::ThingErrorNoError); + }); + + connect(reply, &QModbusReply::errorOccurred, this, [reply] (QModbusDevice::Error error){ + qCWarning(dcAlphaInnotec()) << "Modbus reply error occurred while execute action" << error << reply->errorString(); + emit reply->finished(); // To make sure it will be deleted + }); + } else */ + if (info->action().actionTypeId() == alphaConnectHotWaterSetpointTemperatureActionTypeId) { + double temperature = info->action().paramValue(alphaConnectHotWaterSetpointTemperatureActionHotWaterSetpointTemperatureParamTypeId).toDouble(); + qCDebug(dcAlphaInnotec()) << "Execute action" << info->action().actionTypeId().toString() << info->action().params(); + QModbusReply *reply = connection->setHotWaterSetpointTemperature(temperature); + if (!reply) { + qCWarning(dcAlphaInnotec()) << "Execute action failed because the reply could not be created."; + info->finish(Thing::ThingErrorHardwareFailure); + return; + } + + connect(reply, &QModbusReply::finished, reply, &QModbusReply::deleteLater); + connect(reply, &QModbusReply::finished, info, [info, reply, temperature]{ + if (reply->error() != QModbusDevice::NoError) { + qCWarning(dcAlphaInnotec()) << "Set hot water setpoint temperature finished with error" << reply->errorString(); + info->finish(Thing::ThingErrorHardwareFailure); + return; + } + + qCDebug(dcAlphaInnotec()) << "Execute action finished successfully" << info->action().actionTypeId().toString() << info->action().params(); + info->thing()->setStateValue(alphaConnectHotWaterSetpointTemperatureStateTypeId, temperature); + info->finish(Thing::ThingErrorNoError); + }); + + connect(reply, &QModbusReply::errorOccurred, this, [reply] (QModbusDevice::Error error){ + qCWarning(dcAlphaInnotec()) << "Modbus reply error occurred while execute action" << error << reply->errorString(); + emit reply->finished(); // To make sure it will be deleted + }); + } else if (info->action().actionTypeId() == alphaConnectReturnSetpointTemperatureActionTypeId) { + double temperature = info->action().paramValue(alphaConnectReturnSetpointTemperatureActionReturnSetpointTemperatureParamTypeId).toDouble(); + qCDebug(dcAlphaInnotec()) << "Execute action" << info->action().actionTypeId().toString() << info->action().params(); + QModbusReply *reply = connection->setReturnSetpointTemperature(temperature); + if (!reply) { + qCWarning(dcAlphaInnotec()) << "Execute action failed because the reply could not be created."; + info->finish(Thing::ThingErrorHardwareFailure); + return; + } + + connect(reply, &QModbusReply::finished, reply, &QModbusReply::deleteLater); + connect(reply, &QModbusReply::finished, info, [info, reply, temperature]{ + if (reply->error() != QModbusDevice::NoError) { + qCWarning(dcAlphaInnotec()) << "Set return setpoint temperature finished with error" << reply->errorString(); + info->finish(Thing::ThingErrorHardwareFailure); + return; + } + + qCDebug(dcAlphaInnotec()) << "Execute action finished successfully" << info->action().actionTypeId().toString() << info->action().params(); + info->thing()->setStateValue(alphaConnectReturnSetpointTemperatureStateTypeId, temperature); + info->finish(Thing::ThingErrorNoError); + }); + + connect(reply, &QModbusReply::errorOccurred, this, [reply] (QModbusDevice::Error error){ + qCWarning(dcAlphaInnotec()) << "Modbus reply error occurred while execute action" << error << reply->errorString(); + emit reply->finished(); // To make sure it will be deleted + }); + } else if (info->action().actionTypeId() == alphaConnectSgReadyModeActionTypeId) { + QString sgReadyModeString = info->action().paramValue(alphaConnectSgReadyModeActionSgReadyModeParamTypeId).toString(); + qCDebug(dcAlphaInnotec()) << "Execute action" << info->action().actionTypeId().toString() << info->action().params(); + AlphaConnectModbusTcpConnection::SmartGridState sgReadyState; + if (sgReadyModeString == "Off") { + sgReadyState = AlphaConnectModbusTcpConnection::SmartGridStateOff; + } else if (sgReadyModeString == "Low") { + sgReadyState = AlphaConnectModbusTcpConnection::SmartGridStateLow; + } else if (sgReadyModeString == "High") { + sgReadyState = AlphaConnectModbusTcpConnection::SmartGridStateHigh; + } else { + sgReadyState = AlphaConnectModbusTcpConnection::SmartGridStateStandard; + } + + QModbusReply *reply = connection->setSmartGrid(sgReadyState); + if (!reply) { + qCWarning(dcAlphaInnotec()) << "Execute action failed because the reply could not be created."; + info->finish(Thing::ThingErrorHardwareFailure); + return; + } + + connect(reply, &QModbusReply::finished, reply, &QModbusReply::deleteLater); + connect(reply, &QModbusReply::finished, info, [info, reply, sgReadyModeString]{ + if (reply->error() != QModbusDevice::NoError) { + qCWarning(dcAlphaInnotec()) << "Set SG ready mode finished with error" << reply->errorString(); + info->finish(Thing::ThingErrorHardwareFailure); + return; + } + + qCDebug(dcAlphaInnotec()) << "Execute action finished successfully" << info->action().actionTypeId().toString() << info->action().params(); + info->thing()->setStateValue(alphaConnectSgReadyModeStateTypeId, sgReadyModeString); + info->finish(Thing::ThingErrorNoError); + }); + + connect(reply, &QModbusReply::errorOccurred, this, [reply] (QModbusDevice::Error error){ + qCWarning(dcAlphaInnotec()) << "Modbus reply error occurred while execute action" << error << reply->errorString(); + emit reply->finished(); // To make sure it will be deleted + }); + } + } + + info->finish(Thing::ThingErrorNoError); +} + + diff --git a/alphainnotec/integrationpluginalphainnotec.h b/alphainnotec/integrationpluginalphainnotec.h new file mode 100644 index 0000000..0f6a4cc --- /dev/null +++ b/alphainnotec/integrationpluginalphainnotec.h @@ -0,0 +1,62 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +* +* 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 . +* +* 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 INTEGRATIONPLUGINALPHAINNOTEC_H +#define INTEGRATIONPLUGINALPHAINNOTEC_H + +#include "plugintimer.h" +#include "alphaconnectmodbustcpconnection.h" +#include "integrations/integrationplugin.h" + +class IntegrationPluginAlphaInnotec: public IntegrationPlugin +{ + Q_OBJECT + + Q_PLUGIN_METADATA(IID "io.nymea.IntegrationPlugin" FILE "integrationpluginalphainnotec.json") + Q_INTERFACES(IntegrationPlugin) + +public: + explicit IntegrationPluginAlphaInnotec(); + + void discoverThings(ThingDiscoveryInfo *info) override; + void startMonitoringAutoThings() override; + void setupThing(ThingSetupInfo *info) override; + void postSetupThing(Thing *thing) override; + void thingRemoved(Thing *thing) override; + void executeAction(ThingActionInfo *info) override; + +private: + PluginTimer *m_pluginTimer = nullptr; + QHash m_alpaConnectTcpThings; +}; + +#endif // INTEGRATIONPLUGINALPHAINNOTEC_H + + diff --git a/alphainnotec/integrationpluginalphainnotec.json b/alphainnotec/integrationpluginalphainnotec.json new file mode 100644 index 0000000..14990a4 --- /dev/null +++ b/alphainnotec/integrationpluginalphainnotec.json @@ -0,0 +1,343 @@ +{ + "name": "AlphaInnotec", + "displayName": "alpha innotec", + "id": "b3225a18-e9b0-443e-8c88-802d3132f94d", + "vendors": [ + { + "name": "alphaInnotec", + "displayName": "alpha Innotec", + "id": "2c2101ae-7bb8-4012-97ba-cedf4cecd924", + "thingClasses": [ + { + "name": "alphaConnect", + "displayName": "alpha connect", + "id": "c5437b68-cfd2-4ec8-bad6-006fb5e8a8da", + "createMethods": ["discovery", "user"], + "interfaces": ["smartgridheatpump", "connectable"], + "paramTypes": [ + { + "id": "64a18910-9111-4eaf-986d-f7b64b03b99a", + "name": "ipAddress", + "displayName": "IP address", + "type": "QString", + "inputType": "IPv4Address", + "defaultValue": "127.0.0.1" + }, + { + "id": "f791c219-98a5-41ee-8e5f-1bfb5136dc9c", + "name":"macAddress", + "displayName": "MAC address", + "type": "QString", + "inputType": "MacAddress", + "defaultValue": "" + }, + { + "id": "b92025c1-8978-4d47-bd3c-4df749dbfd0f", + "name":"port", + "displayName": "Port", + "type": "int", + "defaultValue": 502 + }, + { + "id": "1d9517ca-680c-49e2-a8d1-320743c27559", + "name":"slaveId", + "displayName": "Modbus slave ID", + "type": "int", + "defaultValue": 1 + } + ], + "stateTypes": [ + { + "id": "01ce0d61-3813-4c1b-a18b-555913d689a2", + "name": "connected", + "displayName": "Connected", + "displayNameEvent": "Connected changed", + "type": "bool", + "defaultValue": false, + "cached": false + }, + { + "id": "07465fbb-6949-4bd1-90d5-acf2d80c161d", + "name": "heatingOn", + "displayName": "Heating on", + "displayNameEvent": "Heating turned on/off", + "type": "bool", + "defaultValue": false, + "suggestLogging": true + }, + { + "id": "8b407c1d-b84f-48d4-9961-b29bc58fff0e", + "name": "coolingOn", + "displayName": "Cooling on", + "displayNameEvent": "Cooling turned on/off", + "type": "bool", + "defaultValue": false, + "suggestLogging": true + }, + { + "id": "fb448191-69d7-4f2e-87bf-6e4fb61e403f", + "name": "flowTemperature", + "displayName": "Flow temperature", + "displayNameEvent": "Flow temperature changed", + "unit": "DegreeCelsius", + "type": "double", + "defaultValue": 0, + "suggestLogging": true + }, + { + "id": "6f96706e-67a3-43d3-bf74-e2e3e5fd0d6d", + "name": "returnTemperature", + "displayName": "Return temperature", + "displayNameEvent": "Return temperature changed", + "unit": "DegreeCelsius", + "type": "double", + "defaultValue": 0, + "suggestLogging": true + }, + { + "id": "b6702fc5-889b-48b6-9257-64c5ee6e5e95", + "name": "externalReturnTemperature", + "displayName": "External return temperature", + "displayNameEvent": "External return temperature changed", + "unit": "DegreeCelsius", + "type": "double", + "defaultValue": 0, + "suggestLogging": true + }, + { + "id": "da7e6bc9-95fd-4c90-931f-5b1cd69d864a", + "name": "hotWaterTemperature", + "displayName": "Hot water temperature", + "displayNameEvent": "Hot water temperature changed", + "unit": "DegreeCelsius", + "type": "double", + "defaultValue": 0, + "suggestLogging": true + }, + { + "id": "bea6658e-64bf-4476-b75d-38c34a5fed85", + "name": "hotGasTemperature", + "displayName": "Hot gas temperature", + "displayNameEvent": "Hot gas temperature changed", + "unit": "DegreeCelsius", + "type": "double", + "defaultValue": 0, + "suggestLogging": true + }, + { + "id": "e9facc5d-f41c-4256-85aa-b0f777431975", + "name": "heatSourceInletTemperature", + "displayName": "Heat source inlet temperature", + "displayNameEvent": "Heat source inlet temperature changed", + "unit": "DegreeCelsius", + "type": "double", + "defaultValue": 0, + "suggestLogging": true + }, + { + "id": "48424285-4bc4-4110-8089-0486292d7371", + "name": "heatSourceOutletTemperature", + "displayName": "Heat source outlet temperature", + "displayNameEvent": "Heat source outlet temperature changed", + "unit": "DegreeCelsius", + "type": "double", + "defaultValue": 0, + "suggestLogging": true + }, + { + "id": "52f8a581-7b9c-4c15-8aaa-c083763e7820", + "name": "roomTemperature1", + "displayName": "Room remote adjuster 1 temperature", + "displayNameEvent": "Room remote adjuster 1 temperature changed", + "unit": "DegreeCelsius", + "type": "double", + "defaultValue": 0, + "suggestLogging": true + }, + { + "id": "91b57690-5daa-4766-86ae-f205fa0c805a", + "name": "roomTemperature2", + "displayName": "Room remote adjuster 2 temperature", + "displayNameEvent": "Room remote adjuster 2 temperature changed", + "unit": "DegreeCelsius", + "type": "double", + "defaultValue": 0, + "suggestLogging": true + }, + { + "id": "a8c7e706-57ba-4572-b6e6-5eec91065ea8", + "name": "roomTemperature3", + "displayName": "Room remote adjuster 3 temperature", + "displayNameEvent": "Room remote adjuster 3 temperature changed", + "unit": "DegreeCelsius", + "type": "double", + "defaultValue": 0, + "suggestLogging": true + }, + { + "id": "d4484305-eb0f-418b-bb15-f71b08e6fce1", + "name": "solarCollectorTemperature", + "displayName": "Solar collector temperature", + "displayNameEvent": "Solar collector temperature changed", + "unit": "DegreeCelsius", + "type": "double", + "defaultValue": 0, + "suggestLogging": true + }, + { + "id": "75089369-c224-4fe5-91d1-9bdc5578f0a5", + "name": "solarStorageTankTemperature", + "displayName": "Solar storage tank temperature", + "displayNameEvent": "Solar storage tank temperature changed", + "unit": "DegreeCelsius", + "type": "double", + "defaultValue": 0, + "suggestLogging": true + }, + { + "id": "519d7d45-1f3d-4118-ade9-3d7508451005", + "name": "externalEnergySourceTemperature", + "displayName": "External energy source temperature", + "displayNameEvent": "External energy source temperature changed", + "unit": "DegreeCelsius", + "type": "double", + "defaultValue": 0, + "suggestLogging": true + }, + { + "id": "90d88164-3083-47c7-a396-b338bd9ea54c", + "name": "supplyAirTemperature", + "displayName": "Supply air temperature", + "displayNameEvent": "Supply air temperature changed", + "unit": "DegreeCelsius", + "type": "double", + "defaultValue": 0, + "suggestLogging": true + }, + { + "id": "92594f19-8e4c-485b-bfd5-5371a0454267", + "name": "externalAirTemperature", + "displayName": "External air temperature", + "displayNameEvent": "External air temperature changed", + "unit": "DegreeCelsius", + "type": "double", + "defaultValue": 0, + "suggestLogging": true + }, + { + "id": "5a21a131-f3a0-4ebd-9b2e-12eb25c84853", + "name": "systemStatus", + "displayName": "System status", + "displayNameEvent": "System status changed", + "type": "QString", + "possibleValues": [ + "Heating mode", + "Domestic hot water", + "Swimming pool", + "EUV off", + "Defrost", + "Off", + "External energy source", + "Cooling mode" + ], + "defaultValue": "Heating mode", + "suggestLogging": true + }, + { + "id": "ef378ce6-f112-4022-8535-78cbd6ccfeeb", + "name": "heatingPumpOperatingHours", + "displayName": "Heating pump operating hours", + "displayNameEvent": "Heating pump operating hours changed", + "unit": "Hours", + "type": "uint", + "defaultValue": 0 + }, + { + "id": "d0c8f168-49b5-47ca-9988-c9922be38dd5", + "name": "outdoorTemperature", + "displayName": "Outdoor temperature", + "displayNameEvent": "Outdoor temperature changed", + "displayNameAction": "Set outdoor temperature", + "unit": "DegreeCelsius", + "type": "double", + "defaultValue": 0, + "suggestLogging": true + }, + { + "id": "1ee6b189-ceff-4912-a577-2e3320307c3f", + "name": "returnSetpointTemperature", + "displayName": "Return temperature setpoint", + "displayNameEvent": "Return temperature setpoint changed", + "displayNameAction": "Set return temperature setpoint", + "unit": "DegreeCelsius", + "type": "double", + "defaultValue": 0, + "writable": true, + "suggestLogging": true + }, + { + "id": "a7f1a688-51d3-4f9e-a323-86117c5542a8", + "name": "hotWaterSetpointTemperature", + "displayName": "Hot water temperature setpoint", + "displayNameEvent": "Hot water temperature setpoint changed", + "displayNameAction": "Set hot water temperature setpoint", + "unit": "DegreeCelsius", + "type": "double", + "defaultValue": 0, + "writable": true, + "suggestLogging": true + }, + { + "id": "8cc9761f-b30e-4f54-aee7-b64190df57e1", + "name": "sgReadyMode", + "displayName": "Smart grid mode", + "displayNameEvent": "Smart grid mode changed", + "displayNameAction": "Set smart grid mode", + "type": "QString", + "possibleValues": [ + "Off", + "Low", + "Standard", + "High" + ], + "writable": true, + "defaultValue": "Standard", + "suggestLogging": true + }, + { + "id": "c7948cf6-fd1a-44fa-a91b-98f2474ecc62", + "name": "totalEnergy", + "displayName": "Total energy", + "displayNameEvent": "Total energy changed", + "type": "double", + "unit": "KiloWattHour", + "defaultValue": 0, + "suggestLogging": true + }, + { + "id": "24367fba-2883-40da-9b92-5d4db3c71c2f", + "name": "heatingEnergy", + "displayName": "Heating energy", + "displayNameEvent": "Heating energy changed", + "type": "double", + "unit": "KiloWattHour", + "defaultValue": 0, + "suggestLogging": true + }, + { + "id": "be2430c3-54ef-441e-b684-9aabc1d984d6", + "name": "hotWaterEnergy", + "displayName": "Hot water energy", + "displayNameEvent": "Hot water energy changed", + "type": "double", + "unit": "KiloWattHour", + "defaultValue": 0, + "suggestLogging": true + } + ], + "actionTypes": [ ] + } + ] + } + ] +} diff --git a/alphainnotec/meta.json b/alphainnotec/meta.json new file mode 100644 index 0000000..09832c9 --- /dev/null +++ b/alphainnotec/meta.json @@ -0,0 +1,13 @@ +{ + "title": "alpha innotec", + "tagline": "Integrate alpha innotec heat pumps into nymea.", + "icon": "alpha-innotec.png", + "stability": "community", + "offline": true, + "technologies": [ + "network" + ], + "categories": [ + "heating" + ] +} diff --git a/alphainnotec/translations/b3225a18-e9b0-443e-8c88-802d3132f94d-en_US.ts b/alphainnotec/translations/b3225a18-e9b0-443e-8c88-802d3132f94d-en_US.ts new file mode 100644 index 0000000..563414f --- /dev/null +++ b/alphainnotec/translations/b3225a18-e9b0-443e-8c88-802d3132f94d-en_US.ts @@ -0,0 +1,431 @@ + + + + + AlphaInnotec + + + + Connected + The name of the ParamType (ThingClass: alphaConnect, EventType: connected, ID: {01ce0d61-3813-4c1b-a18b-555913d689a2}) +---------- +The name of the StateType ({01ce0d61-3813-4c1b-a18b-555913d689a2}) of ThingClass alphaConnect + + + + + Connected changed + The name of the EventType ({01ce0d61-3813-4c1b-a18b-555913d689a2}) of ThingClass alphaConnect + + + + + + Cooling on + The name of the ParamType (ThingClass: alphaConnect, EventType: coolingOn, ID: {8b407c1d-b84f-48d4-9961-b29bc58fff0e}) +---------- +The name of the StateType ({8b407c1d-b84f-48d4-9961-b29bc58fff0e}) of ThingClass alphaConnect + + + + + Cooling turned on/off + The name of the EventType ({8b407c1d-b84f-48d4-9961-b29bc58fff0e}) of ThingClass alphaConnect + + + + + + External air temperature + The name of the ParamType (ThingClass: alphaConnect, EventType: externalAirTemperature, ID: {92594f19-8e4c-485b-bfd5-5371a0454267}) +---------- +The name of the StateType ({92594f19-8e4c-485b-bfd5-5371a0454267}) of ThingClass alphaConnect + + + + + External air temperature changed + The name of the EventType ({92594f19-8e4c-485b-bfd5-5371a0454267}) of ThingClass alphaConnect + + + + + + External energy source temperature + The name of the ParamType (ThingClass: alphaConnect, EventType: externalEnergySourceTemperature, ID: {519d7d45-1f3d-4118-ade9-3d7508451005}) +---------- +The name of the StateType ({519d7d45-1f3d-4118-ade9-3d7508451005}) of ThingClass alphaConnect + + + + + External energy source temperature changed + The name of the EventType ({519d7d45-1f3d-4118-ade9-3d7508451005}) of ThingClass alphaConnect + + + + + + External return temperature + The name of the ParamType (ThingClass: alphaConnect, EventType: externalReturnTemperature, ID: {b6702fc5-889b-48b6-9257-64c5ee6e5e95}) +---------- +The name of the StateType ({b6702fc5-889b-48b6-9257-64c5ee6e5e95}) of ThingClass alphaConnect + + + + + External return temperature changed + The name of the EventType ({b6702fc5-889b-48b6-9257-64c5ee6e5e95}) of ThingClass alphaConnect + + + + + + Flow temperature + The name of the ParamType (ThingClass: alphaConnect, EventType: flowTemperature, ID: {fb448191-69d7-4f2e-87bf-6e4fb61e403f}) +---------- +The name of the StateType ({fb448191-69d7-4f2e-87bf-6e4fb61e403f}) of ThingClass alphaConnect + + + + + Flow temperature changed + The name of the EventType ({fb448191-69d7-4f2e-87bf-6e4fb61e403f}) of ThingClass alphaConnect + + + + + + Heat source inlet temperature + The name of the ParamType (ThingClass: alphaConnect, EventType: heatSourceInletTemperature, ID: {e9facc5d-f41c-4256-85aa-b0f777431975}) +---------- +The name of the StateType ({e9facc5d-f41c-4256-85aa-b0f777431975}) of ThingClass alphaConnect + + + + + Heat source inlet temperature changed + The name of the EventType ({e9facc5d-f41c-4256-85aa-b0f777431975}) of ThingClass alphaConnect + + + + + + Heat source outlet temperature + The name of the ParamType (ThingClass: alphaConnect, EventType: heatSourceOutletTemperature, ID: {48424285-4bc4-4110-8089-0486292d7371}) +---------- +The name of the StateType ({48424285-4bc4-4110-8089-0486292d7371}) of ThingClass alphaConnect + + + + + Heat source outlet temperature changed + The name of the EventType ({48424285-4bc4-4110-8089-0486292d7371}) of ThingClass alphaConnect + + + + + + Heating on + The name of the ParamType (ThingClass: alphaConnect, EventType: heatingOn, ID: {07465fbb-6949-4bd1-90d5-acf2d80c161d}) +---------- +The name of the StateType ({07465fbb-6949-4bd1-90d5-acf2d80c161d}) of ThingClass alphaConnect + + + + + + Heating pump operating hours + The name of the ParamType (ThingClass: alphaConnect, EventType: heatingPumpOperatingHours, ID: {ef378ce6-f112-4022-8535-78cbd6ccfeeb}) +---------- +The name of the StateType ({ef378ce6-f112-4022-8535-78cbd6ccfeeb}) of ThingClass alphaConnect + + + + + Heating pump operating hours changed + The name of the EventType ({ef378ce6-f112-4022-8535-78cbd6ccfeeb}) of ThingClass alphaConnect + + + + + Heating turned on/off + The name of the EventType ({07465fbb-6949-4bd1-90d5-acf2d80c161d}) of ThingClass alphaConnect + + + + + + Hot gas temperature + The name of the ParamType (ThingClass: alphaConnect, EventType: hotGasTemperature, ID: {bea6658e-64bf-4476-b75d-38c34a5fed85}) +---------- +The name of the StateType ({bea6658e-64bf-4476-b75d-38c34a5fed85}) of ThingClass alphaConnect + + + + + Hot gas temperature changed + The name of the EventType ({bea6658e-64bf-4476-b75d-38c34a5fed85}) of ThingClass alphaConnect + + + + + + Hot water temperature + The name of the ParamType (ThingClass: alphaConnect, EventType: hotWaterTemperature, ID: {da7e6bc9-95fd-4c90-931f-5b1cd69d864a}) +---------- +The name of the StateType ({da7e6bc9-95fd-4c90-931f-5b1cd69d864a}) of ThingClass alphaConnect + + + + + Hot water temperature changed + The name of the EventType ({da7e6bc9-95fd-4c90-931f-5b1cd69d864a}) of ThingClass alphaConnect + + + + + IP address + The name of the ParamType (ThingClass: alphaConnect, Type: thing, ID: {64a18910-9111-4eaf-986d-f7b64b03b99a}) + + + + + MAC address + The name of the ParamType (ThingClass: alphaConnect, Type: thing, ID: {f791c219-98a5-41ee-8e5f-1bfb5136dc9c}) + + + + + + Mean temperature + The name of the ParamType (ThingClass: alphaConnect, EventType: meanTemperature, ID: {956051a6-8677-4b25-b899-71aa3590d4e5}) +---------- +The name of the StateType ({956051a6-8677-4b25-b899-71aa3590d4e5}) of ThingClass alphaConnect + + + + + Mean temperature changed + The name of the EventType ({956051a6-8677-4b25-b899-71aa3590d4e5}) of ThingClass alphaConnect + + + + + Modbus slave ID + The name of the ParamType (ThingClass: alphaConnect, Type: thing, ID: {1d9517ca-680c-49e2-a8d1-320743c27559}) + + + + + + Outdoor temperature + The name of the ParamType (ThingClass: alphaConnect, EventType: outdoorTemperature, ID: {d0c8f168-49b5-47ca-9988-c9922be38dd5}) +---------- +The name of the StateType ({d0c8f168-49b5-47ca-9988-c9922be38dd5}) of ThingClass alphaConnect + + + + + Outdoor temperature changed + The name of the EventType ({d0c8f168-49b5-47ca-9988-c9922be38dd5}) of ThingClass alphaConnect + + + + + Port + The name of the ParamType (ThingClass: alphaConnect, Type: thing, ID: {b92025c1-8978-4d47-bd3c-4df749dbfd0f}) + + + + + + Return temperature + The name of the ParamType (ThingClass: alphaConnect, EventType: returnTemperature, ID: {6f96706e-67a3-43d3-bf74-e2e3e5fd0d6d}) +---------- +The name of the StateType ({6f96706e-67a3-43d3-bf74-e2e3e5fd0d6d}) of ThingClass alphaConnect + + + + + Return temperature changed + The name of the EventType ({6f96706e-67a3-43d3-bf74-e2e3e5fd0d6d}) of ThingClass alphaConnect + + + + + + Room remote adjuster 1 temperature + The name of the ParamType (ThingClass: alphaConnect, EventType: roomTemperature1, ID: {52f8a581-7b9c-4c15-8aaa-c083763e7820}) +---------- +The name of the StateType ({52f8a581-7b9c-4c15-8aaa-c083763e7820}) of ThingClass alphaConnect + + + + + Room remote adjuster 1 temperature changed + The name of the EventType ({52f8a581-7b9c-4c15-8aaa-c083763e7820}) of ThingClass alphaConnect + + + + + + Room remote adjuster 2 temperature + The name of the ParamType (ThingClass: alphaConnect, EventType: roomTemperature2, ID: {91b57690-5daa-4766-86ae-f205fa0c805a}) +---------- +The name of the StateType ({91b57690-5daa-4766-86ae-f205fa0c805a}) of ThingClass alphaConnect + + + + + Room remote adjuster 2 temperature changed + The name of the EventType ({91b57690-5daa-4766-86ae-f205fa0c805a}) of ThingClass alphaConnect + + + + + + Room remote adjuster 3 temperature + The name of the ParamType (ThingClass: alphaConnect, EventType: roomTemperature3, ID: {a8c7e706-57ba-4572-b6e6-5eec91065ea8}) +---------- +The name of the StateType ({a8c7e706-57ba-4572-b6e6-5eec91065ea8}) of ThingClass alphaConnect + + + + + Room remote adjuster 3 temperature changed + The name of the EventType ({a8c7e706-57ba-4572-b6e6-5eec91065ea8}) of ThingClass alphaConnect + + + + + + Room temperature + The name of the ParamType (ThingClass: alphaConnect, EventType: temperature, ID: {0b978a61-4340-443a-8ea6-69217a593508}) +---------- +The name of the StateType ({0b978a61-4340-443a-8ea6-69217a593508}) of ThingClass alphaConnect + + + + + Room temperature changed + The name of the EventType ({0b978a61-4340-443a-8ea6-69217a593508}) of ThingClass alphaConnect + + + + + Set target room temperature + The name of the ActionType ({2e8f44c5-c4e3-4539-b85b-45898bbbf6c2}) of ThingClass alphaConnect + + + + + + Solar collector temperature + The name of the ParamType (ThingClass: alphaConnect, EventType: solarCollectorTemperature, ID: {d4484305-eb0f-418b-bb15-f71b08e6fce1}) +---------- +The name of the StateType ({d4484305-eb0f-418b-bb15-f71b08e6fce1}) of ThingClass alphaConnect + + + + + Solar collector temperature changed + The name of the EventType ({d4484305-eb0f-418b-bb15-f71b08e6fce1}) of ThingClass alphaConnect + + + + + + Solar storage tank temperature + The name of the ParamType (ThingClass: alphaConnect, EventType: solarStorageTankTemperature, ID: {75089369-c224-4fe5-91d1-9bdc5578f0a5}) +---------- +The name of the StateType ({75089369-c224-4fe5-91d1-9bdc5578f0a5}) of ThingClass alphaConnect + + + + + Solar storage tank temperature changed + The name of the EventType ({75089369-c224-4fe5-91d1-9bdc5578f0a5}) of ThingClass alphaConnect + + + + + + Supply air temperature + The name of the ParamType (ThingClass: alphaConnect, EventType: supplyAirTemperature, ID: {90d88164-3083-47c7-a396-b338bd9ea54c}) +---------- +The name of the StateType ({90d88164-3083-47c7-a396-b338bd9ea54c}) of ThingClass alphaConnect + + + + + Supply air temperature changed + The name of the EventType ({90d88164-3083-47c7-a396-b338bd9ea54c}) of ThingClass alphaConnect + + + + + + System status + The name of the ParamType (ThingClass: alphaConnect, EventType: systemStatus, ID: {5a21a131-f3a0-4ebd-9b2e-12eb25c84853}) +---------- +The name of the StateType ({5a21a131-f3a0-4ebd-9b2e-12eb25c84853}) of ThingClass alphaConnect + + + + + System status changed + The name of the EventType ({5a21a131-f3a0-4ebd-9b2e-12eb25c84853}) of ThingClass alphaConnect + + + + + + + Target room temperature + The name of the ParamType (ThingClass: alphaConnect, ActionType: targetTemperature, ID: {2e8f44c5-c4e3-4539-b85b-45898bbbf6c2}) +---------- +The name of the ParamType (ThingClass: alphaConnect, EventType: targetTemperature, ID: {2e8f44c5-c4e3-4539-b85b-45898bbbf6c2}) +---------- +The name of the StateType ({2e8f44c5-c4e3-4539-b85b-45898bbbf6c2}) of ThingClass alphaConnect + + + + + Target room temperature changed + The name of the EventType ({2e8f44c5-c4e3-4539-b85b-45898bbbf6c2}) of ThingClass alphaConnect + + + + + alpha Innotec + The name of the vendor ({2c2101ae-7bb8-4012-97ba-cedf4cecd924}) + + + + + alpha connect + The name of the ThingClass ({c5437b68-cfd2-4ec8-bad6-006fb5e8a8da}) + + + + + alpha innotec + The name of the plugin AlphaInnotec ({b3225a18-e9b0-443e-8c88-802d3132f94d}) + + + + + IntegrationPluginAlphaInnotec + + + The network device discovery is not available. + + + + + No IP address given + + + + diff --git a/debian/control b/debian/control index b5d0f21..e11bcbf 100644 --- a/debian/control +++ b/debian/control @@ -46,6 +46,21 @@ Description: The main libraries and header files for developing with nymea sunsp . This package will install the development files for nymea sunspec. +Package: nymea-plugin-alphainnotec +Architecture: any +Section: libs +Depends: ${shlibs:Depends}, + ${misc:Depends}, + nymea-plugins-modbus-translations +Description: nymea.io plugin for alpha innotec heat pumps + The nymea daemon is a plugin based IoT (Internet of Things) server. The + server works like a translator for devices, things and services and + allows them to interact. + With the powerful rule engine you are able to connect any device available + in the system and create individual scenes and behaviors for your environment. + . + This package will install the nymea.io plugin for alpha innotec heat pumps + Package: nymea-plugin-drexelundweiss Architecture: any diff --git a/debian/nymea-plugin-alphainnotec.install.in b/debian/nymea-plugin-alphainnotec.install.in new file mode 100644 index 0000000..ed59ad5 --- /dev/null +++ b/debian/nymea-plugin-alphainnotec.install.in @@ -0,0 +1 @@ +usr/lib/@DEB_HOST_MULTIARCH@/nymea/plugins/libnymea_integrationpluginalphainnotec.so diff --git a/modbus/modbusdatautils.cpp b/modbus/modbusdatautils.cpp new file mode 100644 index 0000000..d3fea27 --- /dev/null +++ b/modbus/modbusdatautils.cpp @@ -0,0 +1,268 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +* +* Copyright 2013 - 2021, 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 . +* +* 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 "modbusdatautils.h" + +#include + +ModbusDataUtils::ModbusDataUtils() +{ + +} + +quint16 ModbusDataUtils::convertToUInt16(const QVector ®isters) +{ + Q_ASSERT_X(registers.count() == 1, "ModbusDataUtils", "invalid raw data size for converting value to quint16"); + return registers.at(0); +} + +qint16 ModbusDataUtils::convertToInt16(const QVector ®isters) +{ + Q_ASSERT_X(registers.count() == 1, "ModbusDataUtils", "invalid raw data size for converting value to qint16"); + return static_cast(registers.at(0)); +} + +quint32 ModbusDataUtils::convertToUInt32(const QVector ®isters, ByteOrder byteOrder) +{ + Q_ASSERT_X(registers.count() == 2, "ModbusDataUtils", "invalid raw data size for converting value to quint32"); + QByteArray data; + QDataStream inputStream(&data, QIODevice::WriteOnly); + if (byteOrder == ByteOrderBigEndian) { + inputStream << registers.at(0); + inputStream << registers.at(1); + } else { + inputStream << registers.at(1); + inputStream << registers.at(0); + } + + QDataStream outputStream(&data, QIODevice::ReadOnly); + quint32 result = 0; + outputStream >> result; + return result; +} + +qint32 ModbusDataUtils::convertToInt32(const QVector ®isters, ByteOrder byteOrder) +{ + Q_ASSERT_X(registers.count() == 2, "ModbusDataUtils", "invalid raw data size for converting value to quint32"); + QByteArray data; + QDataStream inputStream(&data, QIODevice::WriteOnly); + if (byteOrder == ByteOrderBigEndian) { + inputStream << registers.at(0); + inputStream << registers.at(1); + } else { + inputStream << registers.at(1); + inputStream << registers.at(0); + } + + QDataStream outputStream(&data, QIODevice::ReadOnly); + qint32 result = 0; + outputStream >> result; + return result; +} + +quint64 ModbusDataUtils::convertToUInt64(const QVector ®isters, ByteOrder byteOrder) +{ + Q_ASSERT_X(registers.count() == 4, "ModbusDataUtils", "invalid raw data size for converting value to quint64"); + QByteArray data; + QDataStream inputStream(&data, QIODevice::WriteOnly); + if (byteOrder == ByteOrderBigEndian) { + inputStream << registers.at(0); + inputStream << registers.at(1); + inputStream << registers.at(2); + inputStream << registers.at(3); + } else { + inputStream << registers.at(3); + inputStream << registers.at(2); + inputStream << registers.at(1); + inputStream << registers.at(0); + } + + QDataStream outputStream(&data, QIODevice::ReadOnly); + quint64 result = 0; + outputStream >> result; + return result; +} + +qint64 ModbusDataUtils::convertToInt64(const QVector ®isters, ByteOrder byteOrder) +{ + Q_ASSERT_X(registers.count() == 4, "ModbusDataUtils", "invalid raw data size for converting value to qint64"); + QByteArray data; + QDataStream inputStream(&data, QIODevice::WriteOnly); + if (byteOrder == ByteOrderBigEndian) { + inputStream << registers.at(0); + inputStream << registers.at(1); + inputStream << registers.at(2); + inputStream << registers.at(3); + } else { + inputStream << registers.at(3); + inputStream << registers.at(2); + inputStream << registers.at(1); + inputStream << registers.at(0); + } + + QDataStream outputStream(&data, QIODevice::ReadOnly); + qint64 result = 0; + outputStream >> result; + return result; +} + +QString ModbusDataUtils::convertToString(const QVector ®isters) +{ + QByteArray bytes; + QDataStream stream(&bytes, QIODevice::WriteOnly); + for (int i = 0; i < registers.count(); i++) { + stream << registers.at(i); + } + + return QString::fromUtf8(bytes).trimmed(); +} + +float ModbusDataUtils::convertToFloat32(const QVector ®isters, ByteOrder byteOrder) +{ + Q_ASSERT_X(registers.count() == 2, "ModbusDataUtils", "invalid raw data size for converting value to float32"); + quint32 rawValue = ModbusDataUtils::convertToUInt32(registers, byteOrder); + float value = 0; + memcpy(&value, &rawValue, sizeof(quint32)); + return value; +} + +double ModbusDataUtils::convertToFloat64(const QVector ®isters, ByteOrder byteOrder) +{ + + Q_ASSERT_X(registers.count() == 4, "ModbusDataUtils", "invalid raw data size for converting value to float64"); + quint64 rawValue = ModbusDataUtils::convertToUInt64(registers, byteOrder); + double value = 0; + memcpy(&value, &rawValue, sizeof(quint64)); + return value; +} + +QVector ModbusDataUtils::convertFromUInt16(quint16 value) +{ + return QVector() << value; +} + +QVector ModbusDataUtils::convertFromInt16(qint16 value) +{ + return ModbusDataUtils::convertFromUInt16(static_cast(value)); +} + +QVector ModbusDataUtils::convertFromUInt32(quint32 value, ByteOrder byteOrder) +{ + QByteArray data; + QDataStream inputStream(&data, QIODevice::WriteOnly); + inputStream << value; + + QDataStream outputStream(&data, QIODevice::ReadOnly); + QVector values; + for (int i = 0; i < 2; i++) { + quint16 registerValue = 0; + outputStream >> registerValue; + if (byteOrder == ByteOrderBigEndian) { + values.append(registerValue); + } else { + values.prepend(registerValue); + } + } + + return values; +} + +QVector ModbusDataUtils::convertFromInt32(qint32 value, ByteOrder byteOrder) +{ + return ModbusDataUtils::convertFromUInt32(static_cast(value), byteOrder); +} + +QVector ModbusDataUtils::convertFromUInt64(quint64 value, ByteOrder byteOrder) +{ + QByteArray data; + QDataStream inputStream(&data, QIODevice::WriteOnly); + inputStream << value; + + QDataStream outputStream(&data, QIODevice::ReadOnly); + QVector values; + for (int i = 0; i < 4; i++) { + quint16 registerValue = 0; + outputStream >> registerValue; + if (byteOrder == ByteOrderBigEndian) { + values.append(registerValue); + } else { + values.prepend(registerValue); + } + } + return values; +} + +QVector ModbusDataUtils::convertFromInt64(qint64 value, ByteOrder byteOrder) +{ + QByteArray data; + QDataStream inputStream(&data, QIODevice::WriteOnly); + inputStream << value; + + QDataStream outputStream(&data, QIODevice::ReadOnly); + QVector values; + for (int i = 0; i < 4; i++) { + quint16 registerValue = 0; + outputStream >> registerValue; + if (byteOrder == ByteOrderBigEndian) { + values.append(registerValue); + } else { + values.prepend(registerValue); + } + } + return values; +} + +QVector ModbusDataUtils::convertFromString(const QString &value, quint16 stringLength) +{ + Q_ASSERT_X(value.length() <= stringLength, "ModbusDataUtils", "cannot convert a string which is bigger than the desired register vector."); + QByteArray data = value.toLatin1() + QByteArray('\0', stringLength - value.count()); + QDataStream stream(&data, QIODevice::ReadOnly); + QVector values; + for (int i = 0; i < stringLength; i++) { + quint16 registerValue = 0; + stream >> registerValue; + values.append(registerValue); + } + return values; +} + +QVector ModbusDataUtils::convertFromFloat32(float value, ByteOrder byteOrder) +{ + quint32 rawValue = 0; + memcpy(&rawValue, &value, sizeof(float)); + return ModbusDataUtils::convertFromUInt32(rawValue, byteOrder); +} + +QVector ModbusDataUtils::convertFromFloat64(double value, ByteOrder byteOrder) +{ + quint64 rawValue = 0; + memcpy(&rawValue, &value, sizeof(double)); + return ModbusDataUtils::convertFromUInt64(rawValue, byteOrder); +} diff --git a/modbus/modbusdatautils.h b/modbus/modbusdatautils.h new file mode 100644 index 0000000..f7a4bc6 --- /dev/null +++ b/modbus/modbusdatautils.h @@ -0,0 +1,107 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +* +* Copyright 2013 - 2021, 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 . +* +* 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 MODBUSDATAUTILS_H +#define MODBUSDATAUTILS_H + +#include +#include + +class ModbusDataUtils +{ + Q_GADGET +public: + enum Access { + AccessReadOnly, + AccessWriteOnly, + AccessReadWrite + }; + Q_ENUM(Access) + + enum ByteOrder { + ByteOrderLittleEndian, + ByteOrderBigEndian + }; + Q_ENUM(ByteOrder) + + enum DataType { + UInt8, + UInt16, + Uint32, + Uint64, + Int8, + Int16, + Int32, + Int64, + Float, + Float64, + String, + Bool + }; + Q_ENUM(DataType) + + typedef struct ModbusRegister { + quint16 address; + quint16 size; + DataType dataType; + Access access; + QString description; + QString unit; + QVector rawData; + } ModbusRegister; + + typedef QVector ModbusRegisters; + + explicit ModbusDataUtils(); + + // Convert to + static quint16 convertToUInt16(const QVector ®isters); + static qint16 convertToInt16(const QVector ®isters); + static quint32 convertToUInt32(const QVector ®isters, ByteOrder byteOrder = ByteOrderLittleEndian); + static qint32 convertToInt32(const QVector ®isters, ByteOrder byteOrder = ByteOrderLittleEndian); + static quint64 convertToUInt64(const QVector ®isters, ByteOrder byteOrder = ByteOrderLittleEndian); + static qint64 convertToInt64(const QVector ®isters, ByteOrder byteOrder = ByteOrderLittleEndian); + static QString convertToString(const QVector ®isters); + static float convertToFloat32(const QVector ®isters, ByteOrder byteOrder = ByteOrderLittleEndian); + static double convertToFloat64(const QVector ®isters, ByteOrder byteOrder = ByteOrderLittleEndian); + + // Convert from + static QVector convertFromUInt16(quint16 value); + static QVector convertFromInt16(qint16 value); + static QVector convertFromUInt32(quint32 value, ByteOrder byteOrder = ByteOrderLittleEndian); + static QVector convertFromInt32(qint32 value, ByteOrder byteOrder = ByteOrderLittleEndian); + static QVector convertFromUInt64(quint64 value, ByteOrder byteOrder = ByteOrderLittleEndian); + static QVector convertFromInt64(qint64 value, ByteOrder byteOrder = ByteOrderLittleEndian); + static QVector convertFromString(const QString &value, quint16 stringLength); + static QVector convertFromFloat32(float value, ByteOrder byteOrder = ByteOrderLittleEndian); + static QVector convertFromFloat64(double value, ByteOrder byteOrder = ByteOrderLittleEndian); +}; + +#endif // MODBUSDATAUTILS_H diff --git a/modbus/tools/README.md b/modbus/tools/README.md new file mode 100644 index 0000000..400b082 --- /dev/null +++ b/modbus/tools/README.md @@ -0,0 +1,139 @@ +# Generate a modbus read class + +In order to make the plugin development for modbus TCP devices much easier and faster, a small tool has been developed to generate a modbus TCP master based class providing get and set methods for the registers and property changed signals. + +The workflow looks like this: + +* Write the `registers.json` file containing all register information you are interested to. +* Run the script and provide the class name, output directory and the path to the JSON file +* Include the generated class in your plugin, connect the `Changed()` signal and update the thing state within the plugin. + + +The class will provide 2 main methods for fetching information from the modbus device: + +* `initialize()` will read all registers with `"readSchedule": "init"` and emits the signal `initializationFinished()` once all replies returned. +* `update()` can be used to update all registers with `"readSchedule": "update"`. The class will then fetch each register and update the specified value internally. If the value has changed, the `Changed()` signal will be emitted. + +The reulting class will inhert from the `ModbusTCPMaster` class, providing easy access to all possible modbus operations and inform about the connected state. + + +# JSON format + +The basic structure of the modbus register JSON looks like following example: + +``` +{ + "endianness": "BigEndian", + "enums": [ + { + "name": "NameOfEnum", + "values": [ + { + "key": "EnumValue1", + "value": 0 + }, + { + "key": "EnumValue2", + "value": 1 + }, + .... + ] + } + ], + "registers": [ + { + "id": "registerPropertyName", + "address": 4, + "size": 1, + "type": "uint16", + "readSchedule": "init", + "description": "Description of the register", + "unit": "V", + "defaultValue": "0", + "access": "RO" + }, + { + "id": "registerWithEnumValues", + "address": 5, + "size": 1, + "type": "uint16", + "readSchedule": "update", + "enum": "NameOfEnum", + "defaultValue": "NameOfEnumEnumValue1", + "description": "Description of the enum register like states", + "access": "RO" + }, + ... + ] +} + +``` + +## Endianness + +When converting multiple registers to one data type (i.e. 2 registers uint16 values to one uint32), the order of the registers are important to align with the endiness of the data receiving. + +There are 2 possibilities: + +* `BigEndian`: default if not specified: register bytes come in following order `[0, 1, 2, 3]`: `ABCD` +* `LittleEndian`: register bytes come in following order `[0, 1, 2, 3]`: `CDAB` + +## Enums + +Many modbus devices provide inforation using `Enums`, indicating a special state trough a defined list of values. If a register implements an enum, you can define it in the `enums` section. The `name` property defines the name of the enum, and the script will generate a c++ enum definition from this section. Each enum value will then be generated using ` = `. + +If a register represets an enum, you simply add the property `"enum": "NameOfEnum"` in the register map and the property will be defined using the resulting enum type. All convertion between enum and resulting modbus register value will be done automatically. + +## Registers + +Earch register will be defined as a property in the resulting class modbus TCP class providing easy access to the register data. + +* `id`: Mandatory. The id defines the name of the property used in the resulting class. +* `address`: Mandatory. The modbus address of the register. +* `size`: Mandatory. The amount of registers to read for the property. +* `type`: Mandatory. The data type of this property. Available data types are: + * `uint16` : will be converted to `quint16` + * `int16` : will be converted to `qint16` + * `uint32` : will be converted to `quint32` + * `int32` : will be converted to `qint32` + * `uint64` : will be converted to `quint64` + * `int64` : will be converted to `qint64` + * `float`: will be converted to `float` + * `float64`: will be converted to `double` + * `string` : will be converted to `QString` +* `readSchedule`: Optional. Defines when the register needs to be fetched. If no read schedule has been defined, the class will provide only the update methods, but will not read the value during `initialize()` or `update()` calls. Possible values are: + * `init`: The register will be fetched during initialization. Once all `init `registers have been fetched, the `initializationFinished()` signal will be emitted. + * `update`: The register will be feched each time the `update()` method will be called. +* `enum`: Optional: If the given data type represents an enum value, this propery can be set to the name of the used enum from the `enum` definition. The class will take care internally about the data convertion from and to the enum values. +* `description`: Mandatory. A clear description of the register. +* `unit`: Optional. Represents the unit of this register value. +* `registerType`: Optional. Represents the type of the register and how to read/write it. Default is `holdingRegister`. Possible values are: + * `holdingRegister` + * `inputRegister` + * `coils` + * `discreteInputs` +* `access`: Mandatory. Describes the access to this register. Possible valies are: + * `RO`: Read only access. Only the get method and the changed singal will be defined. + * `RW`: Read and write access. Also a set mehtod will be defined. + * `WO`: Write only. Only the set method will be defined. +* `scaleFactor`: Optional. The name of the scale factor register to convert this value to float. `floatValue = intValue * 10^scaleFactor value`. The scale factor value is normally a `int16` value, i.e. -10 or 10 +* `staticScaleFactor`: Optional. Use this static scale factor to convert this register value to float. `floatValue = registerValue * 10^staticScaleFactor`. The scale factor value is normally a `int16` value, i.e. -10 or 10 +* `defaultValue`: Optional. The value for initializing the property. + +# Example + +Change into your plugin sub directory. +Assuming you wrote the registers.json file you can run now following command to generate your modbus class: + +`$ python3 ../modbus/tools/generate-connection.py -j registers.json -o . -c MyModbusConnection` + +You the result will be a header and a source file called: + +* `mymodbusconnection.h` +* `mymodbusconnection.cpp` + +You can include this class in your project and provide one connection per thing. + + + + diff --git a/modbus/tools/generate-connection.py b/modbus/tools/generate-connection.py new file mode 100644 index 0000000..83f3214 --- /dev/null +++ b/modbus/tools/generate-connection.py @@ -0,0 +1,686 @@ +#!/usr/bin/env python3 + +# Copyright (C) 2021 nymea GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +# To lazy to type all those register plugins, let's make live much easier and generate code from a json register definition + +import os +import re +import sys +import json +import shutil +import argparse +import datetime + +def convertToAlphaNumeric(text): + finalText = '' + for character in text: + if character.isalnum(): + finalText += character + else: + finalText += ' ' + return finalText + + +def splitCamelCase(text): + return re.sub('([A-Z][a-z]+)', r' \1', re.sub('([A-Z]+)', r' \1', text)).split() + + +def convertToCamelCase(text, capitalize = False): + s = convertToAlphaNumeric(text) + s = s.replace("-", " ").replace("_", " ") + words = s.split() + #print('--> words', words) + finalWords = [] + + for i in range(len(words)): + camelCaseSplit = splitCamelCase(words[i]) + if len(camelCaseSplit) == 0: + finalWords.append(words[i]) + else: + #print('--> camel split words', camelCaseSplit) + for j in range(len(camelCaseSplit)): + finalWords.append(camelCaseSplit[j]) + + if len(finalWords) == 0: + return text + + finalText = '' + if capitalize: + finalText = finalWords[0].capitalize() + ''.join(i.capitalize() for i in finalWords[1:]) + else: + finalText = finalWords[0].lower() + ''.join(i.capitalize() for i in finalWords[1:]) + #print('Convert camel case:', text, '-->', finalText) + return finalText + + +def loadJsonFile(filePath): + print('--> Loading JSON file', filePath) + jsonFile = open(filePath, 'r') + return json.load(jsonFile) + + +def writeLine(fileDescriptor, line = ''): + fileDescriptor.write(line + '\n') + + +def writeLicenseHeader(fileDescriptor): + writeLine(fileDescriptor, '/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *') + writeLine(fileDescriptor, '*') + writeLine(fileDescriptor, '* Copyright 2013 - %s, nymea GmbH' % datetime.datetime.now().year) + writeLine(fileDescriptor, '* Contact: contact@nymea.io') + writeLine(fileDescriptor, '*') + writeLine(fileDescriptor, '* This fileDescriptor is part of nymea.') + writeLine(fileDescriptor, '* This project including source code and documentation is protected by') + writeLine(fileDescriptor, '* copyright law, and remains the property of nymea GmbH. All rights, including') + writeLine(fileDescriptor, '* reproduction, publication, editing and translation, are reserved. The use of') + writeLine(fileDescriptor, '* this project is subject to the terms of a license agreement to be concluded') + writeLine(fileDescriptor, '* with nymea GmbH in accordance with the terms of use of nymea GmbH, available') + writeLine(fileDescriptor, '* under https://nymea.io/license') + writeLine(fileDescriptor, '*') + writeLine(fileDescriptor, '* GNU Lesser General Public License Usage') + writeLine(fileDescriptor, '* Alternatively, this project may be redistributed and/or modified under the') + writeLine(fileDescriptor, '* terms of the GNU Lesser General Public License as published by the Free') + writeLine(fileDescriptor, '* Software Foundation; version 3. This project is distributed in the hope that') + writeLine(fileDescriptor, '* it will be useful, but WITHOUT ANY WARRANTY; without even the implied') + writeLine(fileDescriptor, '* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU') + writeLine(fileDescriptor, '* Lesser General Public License for more details.') + writeLine(fileDescriptor, '*') + writeLine(fileDescriptor, '* You should have received a copy of the GNU Lesser General Public License') + writeLine(fileDescriptor, '* along with this project. If not, see .') + writeLine(fileDescriptor, '*') + writeLine(fileDescriptor, '* For any further details and any questions please contact us under') + writeLine(fileDescriptor, '* contact@nymea.io or see our FAQ/Licensing Information on') + writeLine(fileDescriptor, '* https://nymea.io/license/faq') + writeLine(fileDescriptor, '*') + writeLine(fileDescriptor, '* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */') + writeLine(fileDescriptor) + + +def writeEnumDefinition(fileDescriptor, enumDefinition): + print('Writing enum', enumDefinition) + enumName = enumDefinition['name'] + enumValues = enumDefinition['values'] + writeLine(fileDescriptor, ' enum %s {' % enumName) + for i in range(len(enumValues)): + enumData = enumValues[i] + line = (' %s%s = %s' % (enumName, enumData['key'], enumData['value'])) + if i < (len(enumValues) - 1): + line += ',' + + writeLine(fileDescriptor, line) + + writeLine(fileDescriptor, ' };') + writeLine(fileDescriptor, ' Q_ENUM(%s)' % enumName) + writeLine(fileDescriptor) + + +def getCppDataType(registerDefinition, rawType = False): + if not rawType: + if 'enum' in registerDefinition: + return registerDefinition['enum'] + + if 'scaleFactor' in registerDefinition or 'staticScaleFactor' in registerDefinition: + return 'float' + + if registerDefinition['type'] == 'uint16': + return 'quint16' + + if registerDefinition['type'] == 'int16': + return 'qint16' + + if registerDefinition['type'] == 'uint32': + return 'quint32' + + if registerDefinition['type'] == 'int32': + return 'qint32' + + if registerDefinition['type'] == 'uint64': + return 'quint64' + + if registerDefinition['type'] == 'int64': + return 'qint64' + + if registerDefinition['type'] == 'float': + return 'float' + + if registerDefinition['type'] == 'float64': + return 'double' + + if registerDefinition['type'] == 'string': + return 'QString' + + +def getConversionToValueMethod(registerDefinition): + # Handle enums + propertyName = registerDefinition['id'] + propertyTyp = getCppDataType(registerDefinition, True) + + if 'enum' in registerDefinition: + enumName = registerDefinition['enum'] + if registerDefinition['type'] == 'uint16': + return ('ModbusDataUtils::convertFromUInt16(static_cast<%s>(%s))' % (propertyTyp, propertyName)) + elif registerDefinition['type'] == 'int16': + return ('ModbusDataUtils::convertFromInt16(static_cast<%s>(%s))' % (propertyTyp, propertyName)) + elif registerDefinition['type'] == 'uint32': + return ('ModbusDataUtils::convertFromUInt32(static_cast<%s>(%s), ModbusDataUtils::ByteOrder%s)' % (propertyTyp, propertyName, endianness)) + elif registerDefinition['type'] == 'int32': + return ('ModbusDataUtils::convertFromInt32(static_cast<%s>(%s), ModbusDataUtils::ByteOrder%s)' % (propertyTyp, propertyName, endianness)) + + # Handle scale factors + if 'scaleFactor' in registerDefinition: + scaleFactorProperty = 'm_%s' % registerDefinition['scaleFactor'] + if registerDefinition['type'] == 'uint16': + return ('ModbusDataUtils::convertFromUInt16(static_cast<%s>(%s * 1.0 / pow(10, %s)))' % (propertyTyp, propertyName, scaleFactorProperty)) + elif registerDefinition['type'] == 'int16': + return ('ModbusDataUtils::convertFromInt16(static_cast<%s>(%s * 1.0 / pow(10, %s)))' % (propertyTyp, propertyName, scaleFactorProperty)) + elif registerDefinition['type'] == 'uint32': + return ('ModbusDataUtils::convertFromUInt32(static_cast<%s>(%s * 1.0 / pow(10, %s)), ModbusDataUtils::ByteOrder%s)' % (propertyTyp, propertyName, scaleFactorProperty, endianness)) + elif registerDefinition['type'] == 'int32': + return ('ModbusDataUtils::convertFromInt32(static_cast<%s>(%s * 1.0 / pow(10, %s)), ModbusDataUtils::ByteOrder%s)' % (propertyTyp, propertyName, scaleFactorProperty, endianness)) + + elif 'staticScaleFactor' in registerDefinition: + scaleFactor = registerDefinition['staticScaleFactor'] + if registerDefinition['type'] == 'uint16': + return ('ModbusDataUtils::convertFromUInt16(static_cast<%s>(%s * 1.0 / pow(10, %s)))' % (propertyTyp, propertyName, scaleFactor)) + elif registerDefinition['type'] == 'int16': + return ('ModbusDataUtils::convertFromInt16(static_cast<%s>(%s * 1.0 / pow(10, %s)))' % (propertyTyp, propertyName, scaleFactor)) + elif registerDefinition['type'] == 'uint32': + return ('ModbusDataUtils::convertFromUInt32(static_cast<%s>(%s * 1.0 / pow(10, %s)), ModbusDataUtils::ByteOrder%s)' % (propertyTyp, propertyName, scaleFactor, endianness)) + elif registerDefinition['type'] == 'int32': + return ('ModbusDataUtils::convertFromInt32(static_cast<%s>(%s * 1.0 / pow(10, %s)), ModbusDataUtils::ByteOrder%s)' % (propertyTyp, propertyName, scaleFactor, endianness)) + + # Handle default types + elif registerDefinition['type'] == 'uint16': + return ('ModbusDataUtils::convertFromUInt16(%s)' % propertyName) + elif registerDefinition['type'] == 'int16': + return ('ModbusDataUtils::convertFromInt16(%s)' % propertyName) + elif registerDefinition['type'] == 'uint32': + return ('ModbusDataUtils::convertFromUInt32(%s, ModbusDataUtils::ByteOrder%s)' % (propertyName, endianness)) + elif registerDefinition['type'] == 'int32': + return ('ModbusDataUtils::convertFromInt32(%s, ModbusDataUtils::ByteOrder%s)' % (propertyName, endianness)) + elif registerDefinition['type'] == 'uint64': + return ('ModbusDataUtils::convertFromUInt64(%s, ModbusDataUtils::ByteOrder%s)' % (propertyName, endianness)) + elif registerDefinition['type'] == 'int64': + return ('ModbusDataUtils::convertFromInt64(%s, ModbusDataUtils::ByteOrder%s)' % (propertyName, endianness)) + elif registerDefinition['type'] == 'float': + return ('ModbusDataUtils::convertFromFloat32(%s, ModbusDataUtils::ByteOrder%s)' % propertyName, endianness) + elif registerDefinition['type'] == 'float64': + return ('ModbusDataUtils::convertFromFloat64(%s, ModbusDataUtils::ByteOrder%s)' % propertyName, endianness) + elif registerDefinition['type'] == 'string': + return ('ModbusDataUtils::convertFromString(%s)' % propertyName) + + +def getValueConversionMethod(registerDefinition): + # Handle enums + if 'enum' in registerDefinition: + enumName = registerDefinition['enum'] + if registerDefinition['type'] == 'uint16': + return ('static_cast<%s>(ModbusDataUtils::convertToUInt16(unit.values()))' % (enumName)) + elif registerDefinition['type'] == 'int16': + return ('static_cast<%s>(ModbusDataUtils::convertToInt16(unit.values()))' % (enumName)) + elif registerDefinition['type'] == 'uint32': + return ('static_cast<%s>(ModbusDataUtils::convertToUInt32(unit.values(), ModbusDataUtils::ByteOrder%s))' % (enumName, endianness)) + elif registerDefinition['type'] == 'int32': + return ('static_cast<%s>(ModbusDataUtils::convertToInt32(unit.values(), ModbusDataUtils::ByteOrder%s))' % (enumName, endianness)) + + # Handle scale factors + if 'scaleFactor' in registerDefinition: + scaleFactorProperty = 'm_%s' % registerDefinition['scaleFactor'] + if registerDefinition['type'] == 'uint16': + return ('ModbusDataUtils::convertToUInt16(unit.values()) * 1.0 * pow(10, %s)' % (scaleFactorProperty)) + elif registerDefinition['type'] == 'int16': + return ('ModbusDataUtils::convertToInt16(unit.values()) * 1.0 * pow(10, %s)' % (scaleFactorProperty)) + elif registerDefinition['type'] == 'uint32': + return ('ModbusDataUtils::convertToUInt32(unit.values(), ModbusDataUtils::ByteOrder%s) * 1.0 * pow(10, %s)' % (endianness, scaleFactorProperty)) + elif registerDefinition['type'] == 'int32': + return ('ModbusDataUtils::convertToInt32(unit.values(), ModbusDataUtils::ByteOrder%s) * 1.0 * pow(10, %s)' % (endianness, scaleFactorProperty)) + + elif 'staticScaleFactor' in registerDefinition: + scaleFactor = registerDefinition['staticScaleFactor'] + if registerDefinition['type'] == 'uint16': + return ('ModbusDataUtils::convertToUInt16(unit.values()) * 1.0 * pow(10, %s)' % (scaleFactor)) + elif registerDefinition['type'] == 'int16': + return ('ModbusDataUtils::convertToInt16(unit.values()) * 1.0 * pow(10, %s)' % (scaleFactor)) + elif registerDefinition['type'] == 'uint32': + return ('ModbusDataUtils::convertToUInt32(unit.values(), ModbusDataUtils::ByteOrder%s) * 1.0 * pow(10, %s)' % (endianness, scaleFactor)) + elif registerDefinition['type'] == 'int32': + return ('ModbusDataUtils::convertToInt32(unit.values(), ModbusDataUtils::ByteOrder%s) * 1.0 * pow(10, %s)' % (endianness, scaleFactor)) + + # Handle default types + elif registerDefinition['type'] == 'uint16': + return ('ModbusDataUtils::convertToUInt16(unit.values())') + elif registerDefinition['type'] == 'int16': + return ('ModbusDataUtils::convertToInt16(unit.values())') + elif registerDefinition['type'] == 'uint32': + return ('ModbusDataUtils::convertToUInt32(unit.values(), ModbusDataUtils::ByteOrder%s)' % endianness) + elif registerDefinition['type'] == 'int32': + return ('ModbusDataUtils::convertToInt32(unit.values(), ModbusDataUtils::ByteOrder%s)' % endianness) + elif registerDefinition['type'] == 'uint64': + return ('ModbusDataUtils::convertToUInt64(unit.values(), ModbusDataUtils::ByteOrder%s)' % endianness) + elif registerDefinition['type'] == 'int64': + return ('ModbusDataUtils::convertToInt64(unit.values(), ModbusDataUtils::ByteOrder%s)' % endianness) + elif registerDefinition['type'] == 'float': + return ('ModbusDataUtils::convertToFloat32(unit.values(), ModbusDataUtils::ByteOrder%s)' % endianness) + elif registerDefinition['type'] == 'float64': + return ('ModbusDataUtils::convertToFloat64(unit.values(), ModbusDataUtils::ByteOrder%s)' % endianness) + elif registerDefinition['type'] == 'string': + return ('ModbusDataUtils::convertToString(unit.values())') + + +def writePropertyGetSetMethodDeclarations(fileDescriptor, registerDefinitions): + for registerDefinition in registerDefinitions: + propertyName = registerDefinition['id'] + propertyTyp = getCppDataType(registerDefinition) + if 'unit' in registerDefinition and registerDefinition['unit'] != '': + writeLine(fileDescriptor, ' /* %s [%s] - Address: %s, Size: %s */' % (registerDefinition['description'], registerDefinition['unit'], registerDefinition['address'], registerDefinition['size'])) + else: + writeLine(fileDescriptor, ' /* %s - Address: %s, Size: %s */' % (registerDefinition['description'], registerDefinition['address'], registerDefinition['size'])) + + writeLine(fileDescriptor, ' %s %s() const;' % (propertyTyp, propertyName)) + + # Check if we require a set method + if registerDefinition['access'] == 'RW' or registerDefinition['access'] == 'WO': + writeLine(fileDescriptor, ' QModbusReply *set%s(%s %s);' % (propertyName[0].upper() + propertyName[1:], propertyTyp, propertyName)) + + writeLine(fileDescriptor) + + +def writePropertyGetSetMethodImplementations(fileDescriptor, className, registerDefinitions): + for registerDefinition in registerDefinitions: + propertyName = registerDefinition['id'] + propertyTyp = getCppDataType(registerDefinition) + # Get + if 'enum' in registerDefinition: + writeLine(fileDescriptor, '%s::%s %s::%s() const' % (className, propertyTyp, className, propertyName)) + else: + writeLine(fileDescriptor, '%s %s::%s() const' % (propertyTyp, className, propertyName)) + + writeLine(fileDescriptor, '{') + writeLine(fileDescriptor, ' return m_%s;' % propertyName) + writeLine(fileDescriptor, '}') + writeLine(fileDescriptor) + + # Check if we require a set method + if registerDefinition['access'] == 'RW' or registerDefinition['access'] == 'WO': + writeLine(fileDescriptor, 'QModbusReply *%s::set%s(%s %s)' % (className, propertyName[0].upper() + propertyName[1:], propertyTyp, propertyName)) + writeLine(fileDescriptor, '{') + + writeLine(fileDescriptor, ' QVector values = %s;' % getConversionToValueMethod(registerDefinition)) + writeLine(fileDescriptor, ' qCDebug(dc%s()) << "--> Write \\"%s\\" register:" << %s << "size:" << %s << values;' % (className, registerDefinition['description'], registerDefinition['address'], registerDefinition['size'])) + if registerDefinition['registerType'] == 'holdingRegister': + writeLine(fileDescriptor, ' QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::HoldingRegisters, %s, values.count());' % (registerDefinition['address'])) + + # TODO: other write methods + + writeLine(fileDescriptor, ' request.setValues(values);') + writeLine(fileDescriptor, ' return sendWriteRequest(request, m_slaveId);') + writeLine(fileDescriptor, '}') + writeLine(fileDescriptor) + + + +def writePropertyUpdateMethodDeclarations(fileDescriptor, registerDefinitions): + for registerDefinition in registerDefinitions: + if 'readSchedule' in registerDefinition and registerDefinition['readSchedule'] == 'init': + continue + + propertyName = registerDefinition['id'] + propertyTyp = getCppDataType(registerDefinition) + writeLine(fileDescriptor, ' void update%s();' % (propertyName[0].upper() + propertyName[1:])) + + +def writePropertyUpdateMethodImplementations(fileDescriptor, className, registerDefinitions): + for registerDefinition in registerDefinitions: + if 'readSchedule' in registerDefinition and registerDefinition['readSchedule'] == 'init': + continue + + propertyName = registerDefinition['id'] + propertyTyp = getCppDataType(registerDefinition) + writeLine(fileDescriptor, 'void %s::update%s()' % (className, propertyName[0].upper() + propertyName[1:])) + writeLine(fileDescriptor, '{') + writeLine(fileDescriptor, ' // Update registers from %s' % registerDefinition['description']) + writeLine(fileDescriptor, ' qCDebug(dc%s()) << "--> Read \\"%s\\" register:" << %s << "size:" << %s;' % (className, registerDefinition['description'], registerDefinition['address'], registerDefinition['size'])) + writeLine(fileDescriptor, ' QModbusReply *reply = read%s();' % (propertyName[0].upper() + propertyName[1:])) + writeLine(fileDescriptor, ' if (reply) {') + writeLine(fileDescriptor, ' if (!reply->isFinished()) {') + writeLine(fileDescriptor, ' connect(reply, &QModbusReply::finished, reply, &QModbusReply::deleteLater);') + writeLine(fileDescriptor, ' connect(reply, &QModbusReply::finished, this, [this, reply](){') + writeLine(fileDescriptor, ' if (reply->error() == QModbusDevice::NoError) {') + writeLine(fileDescriptor, ' const QModbusDataUnit unit = reply->result();') + writeLine(fileDescriptor, ' qCDebug(dc%s()) << "<-- Response from \\"%s\\" register" << %s << "size:" << %s << unit.values();' % (className, registerDefinition['description'], registerDefinition['address'], registerDefinition['size'])) + + # FIXME: introduce bool and check register type for parsing + writeLine(fileDescriptor, ' %s received%s = %s;' % (propertyTyp, propertyName[0].upper() + propertyName[1:], getValueConversionMethod(registerDefinition))) + writeLine(fileDescriptor, ' if (m_%s != received%s) {' % (propertyName, propertyName[0].upper() + propertyName[1:])) + writeLine(fileDescriptor, ' m_%s = received%s;' % (propertyName, propertyName[0].upper() + propertyName[1:])) + writeLine(fileDescriptor, ' emit %sChanged(m_%s);' % (propertyName, propertyName)) + writeLine(fileDescriptor, ' }') + writeLine(fileDescriptor, ' }') + writeLine(fileDescriptor, ' });') + writeLine(fileDescriptor) + writeLine(fileDescriptor, ' connect(reply, &QModbusReply::errorOccurred, this, [this, reply] (QModbusDevice::Error error){') + writeLine(fileDescriptor, ' qCWarning(dc%s()) << "Modbus reply error occurred while updating \\"%s\\" registers from" << hostAddress().toString() << error << reply->errorString();' % (className, registerDefinition['description'])) + writeLine(fileDescriptor, ' emit reply->finished(); // To make sure it will be deleted') + writeLine(fileDescriptor, ' });') + writeLine(fileDescriptor, ' } else {') + writeLine(fileDescriptor, ' delete reply; // Broadcast reply returns immediatly') + writeLine(fileDescriptor, ' }') + writeLine(fileDescriptor, ' } else {') + writeLine(fileDescriptor, ' qCWarning(dc%s()) << "Error occurred while reading \\"%s\\" registers from" << hostAddress().toString() << errorString();' % (className, registerDefinition['description'])) + writeLine(fileDescriptor, ' }') + writeLine(fileDescriptor, '}') + writeLine(fileDescriptor) + + +def writeInternalPropertyReadMethodDeclarations(fileDescriptor, registerDefinitions): + for registerDefinition in registerDefinitions: + propertyName = registerDefinition['id'] + writeLine(fileDescriptor, ' QModbusReply *read%s();' % (propertyName[0].upper() + propertyName[1:])) + + +def writeInternalPropertyReadMethodImplementations(fileDescriptor, className, registerDefinitions): + for registerDefinition in registerDefinitions: + propertyName = registerDefinition['id'] + writeLine(fileDescriptor, 'QModbusReply *%s::read%s()' % (className, propertyName[0].upper() + propertyName[1:])) + writeLine(fileDescriptor, '{') + + # Build request depending on the register type + if registerDefinition['registerType'] == 'inputRegister': + writeLine(fileDescriptor, ' QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::InputRegisters, %s, %s);' % (registerDefinition['address'], registerDefinition['size'])) + elif registerDefinition['registerType'] == 'discreteInputs': + writeLine(fileDescriptor, ' QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::DiscreteInputs, %s, %s);' % (registerDefinition['address'], registerDefinition['size'])) + elif registerDefinition['registerType'] == 'coils': + writeLine(fileDescriptor, ' QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::Coils, %s, %s);' % (registerDefinition['address'], registerDefinition['size'])) + else: + #Default to holdingRegister + writeLine(fileDescriptor, ' QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::HoldingRegisters, %s, %s);' % (registerDefinition['address'], registerDefinition['size'])) + + writeLine(fileDescriptor, ' return sendReadRequest(request, m_slaveId);') + writeLine(fileDescriptor, '}') + writeLine(fileDescriptor) + + +def writePropertyChangedSignals(fileDescriptor, registerDefinitions): + for registerDefinition in registerDefinitions: + propertyName = registerDefinition['id'] + propertyTyp = getCppDataType(registerDefinition) + if propertyTyp == 'QString': + writeLine(fileDescriptor, ' void %sChanged(const %s &%s);' % (propertyName, propertyTyp, propertyName)) + else: + writeLine(fileDescriptor, ' void %sChanged(%s %s);' % (propertyName, propertyTyp, propertyName)) + + +def writePrivatePropertyMembers(fileDescriptor, registerDefinitions): + for registerDefinition in registerDefinitions: + propertyName = registerDefinition['id'] + propertyTyp = getCppDataType(registerDefinition) + if 'defaultValue' in registerDefinition: + writeLine(fileDescriptor, ' %s m_%s = %s;' % (propertyTyp, propertyName, registerDefinition['defaultValue'])) + else: + writeLine(fileDescriptor, ' %s m_%s;' % (propertyTyp, propertyName)) + + +def writeInitializeMethod(fileDescriptor, className, registerDefinitions): + writeLine(fileDescriptor, 'void %s::initialize()' % (className)) + writeLine(fileDescriptor, '{') + + # First check if there are any init registers + initRequired = False + for registerDefinition in registerDefinitions: + if registerDefinition['readSchedule'] == 'init': + initRequired = True + break + + if initRequired: + writeLine(fileDescriptor, ' QModbusReply *reply = nullptr;') + writeLine(fileDescriptor) + writeLine(fileDescriptor, ' if (!m_pendingInitReplies.isEmpty()) {') + writeLine(fileDescriptor, ' qCWarning(dc%s()) << "Tried to initialize but there are still some init replies pending.";' % className) + writeLine(fileDescriptor, ' return;') + writeLine(fileDescriptor, ' }') + + for registerDefinition in registerDefinitions: + propertyName = registerDefinition['id'] + propertyTyp = getCppDataType(registerDefinition) + + if 'readSchedule' in registerDefinition and registerDefinition['readSchedule'] == 'init': + writeLine(fileDescriptor) + writeLine(fileDescriptor, ' // Read %s' % registerDefinition['description']) + writeLine(fileDescriptor, ' reply = read%s();' % (propertyName[0].upper() + propertyName[1:])) + writeLine(fileDescriptor, ' if (reply) {') + writeLine(fileDescriptor, ' if (!reply->isFinished()) {') + writeLine(fileDescriptor, ' m_pendingInitReplies.append(reply);') + writeLine(fileDescriptor, ' connect(reply, &QModbusReply::finished, reply, &QModbusReply::deleteLater);') + writeLine(fileDescriptor, ' connect(reply, &QModbusReply::finished, this, [this, reply](){') + writeLine(fileDescriptor, ' if (reply->error() == QModbusDevice::NoError) {') + writeLine(fileDescriptor, ' const QModbusDataUnit unit = reply->result();') + writeLine(fileDescriptor, ' %s received%s = %s;' % (propertyTyp, propertyName[0].upper() + propertyName[1:], getValueConversionMethod(registerDefinition))) + writeLine(fileDescriptor, ' if (m_%s != received%s) {' % (propertyName, propertyName[0].upper() + propertyName[1:])) + writeLine(fileDescriptor, ' m_%s = received%s;' % (propertyName, propertyName[0].upper() + propertyName[1:])) + writeLine(fileDescriptor, ' emit %sChanged(m_%s);' % (propertyName, propertyName)) + writeLine(fileDescriptor, ' }') + writeLine(fileDescriptor, ' }') + writeLine(fileDescriptor) + writeLine(fileDescriptor, ' m_pendingInitReplies.removeAll(reply);') + writeLine(fileDescriptor, ' verifyInitFinished();') + writeLine(fileDescriptor, ' });') + writeLine(fileDescriptor) + writeLine(fileDescriptor, ' connect(reply, &QModbusReply::errorOccurred, this, [this, reply] (QModbusDevice::Error error){') + writeLine(fileDescriptor, ' qCWarning(dc%s()) << "Modbus reply error occurred while reading \\"%s\\" registers from" << hostAddress().toString() << error << reply->errorString();' % (className, registerDefinition['description'])) + writeLine(fileDescriptor, ' emit reply->finished(); // To make sure it will be deleted') + writeLine(fileDescriptor, ' });') + writeLine(fileDescriptor, ' } else {') + writeLine(fileDescriptor, ' delete reply; // Broadcast reply returns immediatly') + writeLine(fileDescriptor, ' }') + writeLine(fileDescriptor, ' } else {') + writeLine(fileDescriptor, ' qCWarning(dc%s()) << "Error occurred while reading \\"%s\\" registers from" << hostAddress().toString() << errorString();' % (className, registerDefinition['description'])) + writeLine(fileDescriptor, ' }') + + + else: + writeLine(fileDescriptor, ' // No init registers defined. Nothing to be done and we are finished.') + writeLine(fileDescriptor, ' emit initializationFinished();') + + writeLine(fileDescriptor, '}') + writeLine(fileDescriptor) + + +def writeUpdateMethod(fileDescriptor, className, registerDefinitions): + writeLine(fileDescriptor, 'void %s::update()' % (className)) + writeLine(fileDescriptor, '{') + for registerDefinition in registerDefinitions: + propertyName = registerDefinition['id'] + if 'readSchedule' in registerDefinition and registerDefinition['readSchedule'] == 'update': + writeLine(fileDescriptor, ' update%s();' % (propertyName[0].upper() + propertyName[1:])) + + writeLine(fileDescriptor, '}') + writeLine(fileDescriptor) + + +############################################################################################ +# Main +############################################################################################ + +parser = argparse.ArgumentParser(description='Generate modbus tcp connection class from JSON register definitions file.') +parser.add_argument('-j', '--json', metavar='', help='The JSON file containing the register definitions.') +parser.add_argument('-o', '--output-directory', metavar='', help='The output directory for the resulting class.') +parser.add_argument('-c', '--class-name', metavar='', help='The name of the resulting class.') +args = parser.parse_args() + +registerJson = loadJsonFile(args.json) +scriptPath = os.path.dirname(os.path.realpath(sys.argv[0])) +outputDirectory = os.path.realpath(args.output_directory) +className = args.class_name + +headerFileName = className.lower() + '.h' +sourceFileName = className.lower() + '.cpp' + +headerFilePath = os.path.join(outputDirectory, headerFileName) +sourceFilePath = os.path.join(outputDirectory, sourceFileName) + +print('Scrip path: %s' % scriptPath) +print('Output directory: %s' % outputDirectory) +print('Class name: %s' % className) +print('Header file: %s' % headerFileName) +print('Source file: %s' % sourceFileName) +print('Header file path: %s' % headerFilePath) +print('Source file path: %s' % sourceFilePath) + +endianness = 'BigEndian' +if 'endianness' in registerJson: + endianness = registerJson['endianness'] + + +############################################################################# +# Write header file +############################################################################# + +headerFile = open(headerFilePath, 'w') + +writeLicenseHeader(headerFile) +writeLine(headerFile, '#ifndef %s_H' % className.upper()) +writeLine(headerFile, '#define %s_H' % className.upper()) +writeLine(headerFile) +writeLine(headerFile, '#include ') +writeLine(headerFile) +writeLine(headerFile, '#include "../modbus/modbusdatautils.h"') +writeLine(headerFile, '#include "../modbus/modbustcpmaster.h"') + +writeLine(headerFile) + +# Begin of class +writeLine(headerFile, 'class %s : public ModbusTCPMaster' % className) +writeLine(headerFile, '{') +writeLine(headerFile, ' Q_OBJECT') + +# Public members +writeLine(headerFile, 'public:') + +# Enum declarations +for enumDefinition in registerJson['enums']: + writeEnumDefinition(headerFile, enumDefinition) + +# Constructor +writeLine(headerFile, ' explicit %s(const QHostAddress &hostAddress, uint port, quint16 slaveId, QObject *parent = nullptr);' % className) +writeLine(headerFile, ' ~%s() = default;' % className) +writeLine(headerFile) + +# Write registers get method declarations +writePropertyGetSetMethodDeclarations(headerFile, registerJson['registers']) + +# Write init and update method declarations +writeLine(headerFile, ' virtual void initialize();') +writeLine(headerFile, ' virtual void update();') +writeLine(headerFile) + +writePropertyUpdateMethodDeclarations(headerFile, registerJson['registers']) +writeLine(headerFile) + +# Write registers value changed signals +writeLine(headerFile, 'signals:') +writeLine(headerFile, ' void initializationFinished();') +writeLine(headerFile) +writePropertyChangedSignals(headerFile, registerJson['registers']) +writeLine(headerFile) + +# Private members +writeLine(headerFile, 'private:') +writeLine(headerFile, ' quint16 m_slaveId = 1;') +writeLine(headerFile, ' QVector m_pendingInitReplies;') +writeLine(headerFile) +writePrivatePropertyMembers(headerFile, registerJson['registers']) +writeLine(headerFile) +writeLine(headerFile, ' void verifyInitFinished();') +writeLine(headerFile) +writeInternalPropertyReadMethodDeclarations(headerFile, registerJson['registers']) +writeLine(headerFile) + +# End of class +writeLine(headerFile) +writeLine(headerFile, '};') +writeLine(headerFile) +writeLine(headerFile, 'QDebug operator<<(QDebug debug, %s *%s);' % (className, className[0].lower() + className[1:])) +writeLine(headerFile) +writeLine(headerFile, '#endif // %s_H' % className.upper()) + +headerFile.close() + + + +############################################################################# +# Write source file +############################################################################# + +sourceFile = open(sourceFilePath, 'w') +writeLicenseHeader(sourceFile) +writeLine(sourceFile) +writeLine(sourceFile, '#include "%s"' % headerFileName) +writeLine(sourceFile, '#include "loggingcategories.h"') +writeLine(sourceFile) +writeLine(sourceFile, 'NYMEA_LOGGING_CATEGORY(dc%s, "%s")' % (className, className)) +writeLine(sourceFile) + +# Constructor +writeLine(sourceFile, '%s::%s(const QHostAddress &hostAddress, uint port, quint16 slaveId, QObject *parent) :' % (className, className)) +writeLine(sourceFile, ' ModbusTCPMaster(hostAddress, port, parent),') +writeLine(sourceFile, ' m_slaveId(slaveId)') +writeLine(sourceFile, '{') +writeLine(sourceFile, ' ') +writeLine(sourceFile, '}') +writeLine(sourceFile) + +# Property get methods +writePropertyGetSetMethodImplementations(sourceFile, className, registerJson['registers']) + +# Write init and update method implementation +writeInitializeMethod(sourceFile, className, registerJson['registers']) +writeUpdateMethod(sourceFile, className, registerJson['registers']) + +# Write update methods +writePropertyUpdateMethodImplementations(sourceFile, className, registerJson['registers']) + +# Write property read method implementations +writeInternalPropertyReadMethodImplementations(sourceFile, className, registerJson['registers']) + +writeLine(sourceFile, 'void %s::verifyInitFinished()' % (className)) +writeLine(sourceFile, '{') +writeLine(sourceFile, ' if (m_pendingInitReplies.isEmpty()) {') +writeLine(sourceFile, ' qCDebug(dc%s()) << "Initialization finished of %s" << hostAddress().toString();' % (className, className)) +writeLine(sourceFile, ' emit initializationFinished();') +writeLine(sourceFile, ' }') +writeLine(sourceFile, '}') +writeLine(sourceFile) + +# Write the debug print +debugObjectParamName = className[0].lower() + className[1:] +writeLine(sourceFile, 'QDebug operator<<(QDebug debug, %s *%s)' % (className, debugObjectParamName)) +writeLine(sourceFile, '{') +writeLine(sourceFile, ' debug.nospace().noquote() << "%s(" << %s->hostAddress().toString() << ":" << %s->port() << ")" << "\\n";' % (className, debugObjectParamName, debugObjectParamName)) +for registerDefinition in registerJson['registers']: + propertyName = registerDefinition['id'] + propertyTyp = getCppDataType(registerDefinition) + line = ('" - %s:" << %s->%s()' % (registerDefinition['description'], debugObjectParamName, propertyName)) + if 'unit' in registerDefinition and registerDefinition['unit'] != '': + line += (' << " [%s]"' % registerDefinition['unit']) + writeLine(sourceFile, ' debug.nospace().noquote() << %s << "\\n";' % (line)) + +writeLine(sourceFile, ' return debug.quote().space();') +writeLine(sourceFile, '}') +writeLine(sourceFile) + +sourceFile.close() \ No newline at end of file diff --git a/nymea-plugins-modbus.pro b/nymea-plugins-modbus.pro index 2afbded..d27afcf 100644 --- a/nymea-plugins-modbus.pro +++ b/nymea-plugins-modbus.pro @@ -5,6 +5,7 @@ CONFIG += ordered SUBDIRS += libnymea-sunspec PLUGIN_DIRS = \ + alphainnotec \ drexelundweiss \ energymeters \ modbuscommander \