diff --git a/idm/idm.cpp b/idm/idm.cpp index c21bd24..799a3de 100644 --- a/idm/idm.cpp +++ b/idm/idm.cpp @@ -46,6 +46,7 @@ Idm::Idm(const QHostAddress &address, QObject *parent) : connect(m_modbusMaster, &ModbusTCPMaster::receivedHoldingRegister, this, &Idm::onReceivedHoldingRegister); connect(m_modbusMaster, &ModbusTCPMaster::readRequestError, this, &Idm::onModbusError); connect(m_modbusMaster, &ModbusTCPMaster::writeRequestError, this, &Idm::onModbusError); + connect(m_modbusMaster, &ModbusTCPMaster::writeRequestExecuted, this, &Idm::writeRequestExecuted); } } @@ -65,6 +66,18 @@ QHostAddress Idm::getIdmAddress() const return m_hostAddress; } +void Idm::getStatus() +{ + //this request starts an update cycle + m_modbusMaster->readHoldingRegister(Idm::modbusUnitID, Idm::OutsideTemperature, 2); +} + +QUuid Idm::setTargetTemperature(double targetTemperature) +{ + QVector value = ModbusHelpers::convertFloatToRegister(targetTemperature); + return m_modbusMaster->writeHoldingRegisters(Idm::modbusUnitID, Idm::RegisterList::RoomTemperatureTargetHeatingEcoHKA, value); +} + void Idm::onReceivedHoldingRegister(int slaveAddress, int modbusRegister, const QVector &value) { Q_UNUSED(slaveAddress); @@ -74,81 +87,81 @@ void Idm::onReceivedHoldingRegister(int slaveAddress, int modbusRegister, const case Idm::OutsideTemperature: /* qCDebug(dcIdm()) << "received outside temperature"; */ if (value.length() == 2) { - m_info.outsideTemperature = ModbusHelpers::convertRegisterToFloat(&value[RegisterList::OutsideTemperature - modbusRegister]); + m_idmInfo.outsideTemperature = ModbusHelpers::convertRegisterToFloat(&value[RegisterList::OutsideTemperature - modbusRegister]); } QTimer::singleShot(200, this, [this] { - m_modbusMaster->readHoldingRegister(Idm::ModbusUnitID, Idm::CurrentFaultNumber, 1); + m_modbusMaster->readHoldingRegister(Idm::modbusUnitID, Idm::CurrentFaultNumber, 1); }); break; case Idm::CurrentFaultNumber: /* qCDebug(dcIdm()) << "current fault number"; */ if (value.length() == 1) { if (value[0] > 0) { - m_info.error = true; + m_idmInfo.error = true; } else { - m_info.error = false; + m_idmInfo.error = false; } } QTimer::singleShot(200, this, [this] { - m_modbusMaster->readHoldingRegister(Idm::ModbusUnitID, Idm::HeatStorageTemperature, 2); + m_modbusMaster->readHoldingRegister(Idm::modbusUnitID, Idm::HeatStorageTemperature, 2); }); break; case Idm::HeatStorageTemperature: /* qCDebug(dcIdm()) << "received storage temperature"; */ if (value.length() == 2) { - m_info.waterTemperature = ModbusHelpers::convertRegisterToFloat(&value[RegisterList::HeatStorageTemperature - modbusRegister]); + m_idmInfo.waterTemperature = ModbusHelpers::convertRegisterToFloat(&value[RegisterList::HeatStorageTemperature - modbusRegister]); } QTimer::singleShot(200, this, [this] { - m_modbusMaster->readHoldingRegister(Idm::ModbusUnitID, Idm::TargetHotWaterTemperature, 1); + m_modbusMaster->readHoldingRegister(Idm::modbusUnitID, Idm::TargetHotWaterTemperature, 1); }); break; case Idm::TargetHotWaterTemperature: /* qCDebug(dcIdm()) << "received target hot water temperature"; */ if (value.length() == 1) { /* The hot water target temperature is stored as UCHAR (manual p. 13) */ - m_info.targetWaterTemperature = (double)value[RegisterList::TargetHotWaterTemperature - modbusRegister]; + m_idmInfo.targetWaterTemperature = (double)value[RegisterList::TargetHotWaterTemperature - modbusRegister]; } QTimer::singleShot(200, this, [this] { - m_modbusMaster->readHoldingRegister(Idm::ModbusUnitID, Idm::HeatPumpOperatingMode, 1); + m_modbusMaster->readHoldingRegister(Idm::modbusUnitID, Idm::HeatPumpOperatingMode, 1); }); break; case Idm::HeatPumpOperatingMode: /* qCDebug(dcIdm()) << "received heat pump operating mode"; */ if (value.length() == 1) { - m_info.mode = heatPumpOperationModeToString((Idm::IdmHeatPumpMode)value[RegisterList::HeatPumpOperatingMode-modbusRegister]); + m_idmInfo.mode = heatPumpOperationModeToString((Idm::IdmHeatPumpMode)value[RegisterList::HeatPumpOperatingMode-modbusRegister]); } QTimer::singleShot(200, this, [this] { - m_modbusMaster->readHoldingRegister(Idm::ModbusUnitID, Idm::RoomTemperatureHKA, 2); + m_modbusMaster->readHoldingRegister(Idm::modbusUnitID, Idm::RoomTemperatureHKA, 2); }); break; case Idm::RoomTemperatureHKA: /* qCDebug(dcIdm()) << "received room temperature hka"; */ if (value.length() == 2) { - m_info.roomTemperature = ModbusHelpers::convertRegisterToFloat(&value[RegisterList::RoomTemperatureHKA - modbusRegister]); + m_idmInfo.roomTemperature = ModbusHelpers::convertRegisterToFloat(&value[RegisterList::RoomTemperatureHKA - modbusRegister]); } QTimer::singleShot(200, this, [this] { - m_modbusMaster->readHoldingRegister(Idm::ModbusUnitID, Idm::RoomTemperatureTargetHeatingEcoHKA, 2); + m_modbusMaster->readHoldingRegister(Idm::modbusUnitID, Idm::RoomTemperatureTargetHeatingEcoHKA, 2); }); break; case Idm::RoomTemperatureTargetHeatingEcoHKA: /* qCDebug(dcIdm()) << "received room temprature hka eco"; */ if (value.length() == 2) { - m_info.targetRoomTemperature = ModbusHelpers::convertRegisterToFloat(&value[RegisterList::RoomTemperatureTargetHeatingEcoHKA - modbusRegister]); + m_idmInfo.targetRoomTemperature = ModbusHelpers::convertRegisterToFloat(&value[RegisterList::RoomTemperatureTargetHeatingEcoHKA - modbusRegister]); } QTimer::singleShot(200, this, [this] { - m_modbusMaster->readHoldingRegister(Idm::ModbusUnitID, Idm::CurrentPowerConsumptionHeatPump, 2); + m_modbusMaster->readHoldingRegister(Idm::modbusUnitID, Idm::CurrentPowerConsumptionHeatPump, 2); }); break; case Idm::CurrentPowerConsumptionHeatPump: /* qCDebug(dcIdm()) << "received power consumption heat pump"; */ if (value.length() == 2) { - m_info.powerConsumptionHeatPump = ModbusHelpers::convertRegisterToFloat(&value[RegisterList::CurrentPowerConsumptionHeatPump - modbusRegister]); + m_idmInfo.powerConsumptionHeatPump = ModbusHelpers::convertRegisterToFloat(&value[RegisterList::CurrentPowerConsumptionHeatPump - modbusRegister]); } /* Everything read without an error * -> set connected to true */ - m_info.connected = true; - emit statusUpdated(m_info); + m_idmInfo.connected = true; + emit statusUpdated(m_idmInfo); break; } } @@ -156,13 +169,8 @@ void Idm::onReceivedHoldingRegister(int slaveAddress, int modbusRegister, const void Idm::onModbusError() { qCDebug(dcIdm()) << "iDM: Received modbus error"; - m_info.connected = false; - emit statusUpdated(m_info); -} - -void Idm::onRequestStatus() -{ - m_modbusMaster->readHoldingRegister(Idm::ModbusUnitID, Idm::OutsideTemperature, 2); + m_idmInfo.connected = false; + emit statusUpdated(m_idmInfo); } QString Idm::systemOperationModeToString(IdmSysMode mode) diff --git a/idm/idm.h b/idm/idm.h index b9699d4..6e669ce 100644 --- a/idm/idm.h +++ b/idm/idm.h @@ -73,12 +73,12 @@ public: bool connectDevice(); QHostAddress getIdmAddress() const; - bool setTargetTemperature; + QUuid setTargetTemperature(double targetTemperature); + void getStatus(); private: - /** Modbus Unit ID of Idm device */ - static const quint16 ModbusUnitID = 1; + static const quint16 modbusUnitID = 1; enum IscModus { KeineAbwarme = 0, @@ -160,12 +160,12 @@ private: * within the IntegrationPluginIdm class. */ QHostAddress m_hostAddress; - /** Pointer to ModbusTCPMaster object, responseible for low-level communicaiton */ + /** Pointer to ModbusTCPMaster object, responsible for low-level communicaiton */ ModbusTCPMaster *m_modbusMaster = nullptr; /** This structure is allocated within onRequestStatus and filled * by the receivedStatusGroupx functions */ - IdmInfo m_info; + IdmInfo m_idmInfo; /** Converts a system operation mode code to a string (according to manual p. 13) */ QString systemOperationModeToString(IdmSysMode mode); @@ -176,10 +176,10 @@ private: signals: void statusUpdated(const IdmInfo &info); void targetRoomTemperatureChanged(); + void writeRequestExecuted(const QUuid &requestId, bool success); -public slots: +private slots: void onModbusError(); - void onRequestStatus(); void onReceivedHoldingRegister(int slaveAddress, int modbusRegister, const QVector &value); }; diff --git a/idm/integrationpluginidm.cpp b/idm/integrationpluginidm.cpp index 5e02ebb..db4a875 100644 --- a/idm/integrationpluginidm.cpp +++ b/idm/integrationpluginidm.cpp @@ -62,7 +62,7 @@ void IntegrationPluginIdm::setupThing(ThingSetupInfo *info) Q_FOREACH (Idm *idm, m_idmConnections) { if (hostAddress.isEqual(idm->getIdmAddress())) { qCWarning(dcIdm()) << "Address already in use"; - info->finish(Thing::ThingErrorSetupFailed, "IP address already in use"); + info->finish(Thing::ThingErrorSetupFailed, QT_TR_NOOP("IP address already in use")); return; } } @@ -75,15 +75,18 @@ void IntegrationPluginIdm::setupThing(ThingSetupInfo *info) info->finish(Thing::ThingErrorHardwareNotAvailable); return; } - m_idmConnections.insert(thing, idm); - connect(idm, &Idm::statusUpdated, info, [info] (const IdmInfo &idmInfo) { + + connect(idm, &Idm::statusUpdated, info, [info, thing, idm, this] (const IdmInfo &idmInfo) { if (idmInfo.connected) { + m_idmConnections.insert(thing, idm); + connect(idm, &Idm::statusUpdated, this, &IntegrationPluginIdm::onStatusUpdated); + connect(idm, &Idm::writeRequestExecuted, this, &IntegrationPluginIdm::onWriteRequestExecuted); info->finish(Thing::ThingErrorNoError); } }); connect(idm, &Idm::destroyed, this, [thing, this] {m_idmConnections.remove(thing);}); connect(info, &ThingSetupInfo::aborted, idm, &Idm::deleteLater); - connect(idm, &Idm::statusUpdated, this, &IntegrationPluginIdm::onStatusUpdated); + } else { Q_ASSERT_X(false, "setupThing", QString("Unhandled thingClassId: %1").arg(thing->thingClassId().toString()).toUtf8()); @@ -101,16 +104,14 @@ void IntegrationPluginIdm::postSetupThing(Thing *thing) } if (thing->thingClassId() == navigator2ThingClassId) { - qCDebug(dcIdm()) << "Thing id: " << thing->id(); Idm *idm = m_idmConnections.value(thing); - - if (idm != nullptr) { - - qCDebug(dcIdm()) << "Thing set up, calling update"; - update(thing); - - thing->setStateValue(navigator2ConnectedStateTypeId, true); + if (!idm) { + qCWarning(dcIdm()) << "Could not find any iDM connection for" << thing->name(); + return; } + + thing->setStateValue(navigator2ConnectedStateTypeId, true); + update(thing); } } @@ -137,9 +138,14 @@ void IntegrationPluginIdm::executeAction(ThingActionInfo *info) Action action = info->action(); if (thing->thingClassId() == navigator2ThingClassId) { + Idm *idm = m_idmConnections.value(thing); + if (!idm) { + return info->finish(Thing::ThingErrorHardwareFailure); + } if (action.actionTypeId() == navigator2TargetTemperatureActionTypeId) { double targetTemperature = thing->stateValue(navigator2TargetTemperatureStateTypeId).toDouble(); - Q_UNUSED(targetTemperature); + QUuid requestId = idm->setTargetTemperature(targetTemperature); + m_asyncActions.insert(requestId, info); } else { Q_ASSERT_X(false, "executeAction", QString("Unhandled action: %1").arg(action.actionTypeId().toString()).toUtf8()); @@ -152,13 +158,13 @@ void IntegrationPluginIdm::executeAction(ThingActionInfo *info) void IntegrationPluginIdm::update(Thing *thing) { if (thing->thingClassId() == navigator2ThingClassId) { - qCDebug(dcIdm()) << "Updating thing"; + qCDebug(dcIdm()) << "Updating thing" << thing->name(); Idm *idm = m_idmConnections.value(thing); - - if (idm != nullptr) { - idm->onRequestStatus(); + if (!idm) { + return; } + idm->getStatus(); } } @@ -186,6 +192,18 @@ void IntegrationPluginIdm::onStatusUpdated(const IdmInfo &info) thing->setStateValue(navigator2ErrorStateTypeId, info.error); } +void IntegrationPluginIdm::onWriteRequestExecuted(const QUuid &requestId, bool success) +{ + if (m_asyncActions.contains(requestId)) { + ThingActionInfo *info = m_asyncActions.value(requestId); + if (success) { + info->finish(Thing::ThingErrorNoError); + } else { + info->finish(Thing::ThingErrorHardwareNotAvailable); + } + } +} + void IntegrationPluginIdm::onRefreshTimer() { qCDebug(dcIdm()) << "onRefreshTimer called"; diff --git a/idm/integrationpluginidm.h b/idm/integrationpluginidm.h index 351be4c..808edb8 100644 --- a/idm/integrationpluginidm.h +++ b/idm/integrationpluginidm.h @@ -38,7 +38,6 @@ #include - class IntegrationPluginIdm: public IntegrationPlugin { Q_OBJECT @@ -64,6 +63,8 @@ private: private slots: void onStatusUpdated(const IdmInfo &info); + void onWriteRequestExecuted(const QUuid &requestId, bool success); + }; #endif // INTEGRATIONPLUGINIDM_H diff --git a/modbus/modbushelpers.cpp b/modbus/modbushelpers.cpp index 8e69cf2..043b671 100644 --- a/modbus/modbushelpers.cpp +++ b/modbus/modbushelpers.cpp @@ -31,6 +31,7 @@ #include "modbushelpers.h" float ModbusHelpers::convertRegisterToFloat(const quint16 *reg) { + float result = 0.0; if (reg != nullptr) { @@ -47,12 +48,14 @@ float ModbusHelpers::convertRegisterToFloat(const quint16 *reg) { return result; } -void ModbusHelpers::convertFloatToRegister(QVector ®, float value) { +QVector ModbusHelpers::convertFloatToRegister(float value) +{ quint32 tmp = 0; - memcpy((char *)&tmp, (char *)&value, sizeof(value)); + QVector reg; reg.append((quint16)(tmp)); reg.append((quint16)((tmp & 0xFFFF0000) >> 16)); + return reg; } diff --git a/modbus/modbushelpers.h b/modbus/modbushelpers.h index 540daed..bae33f3 100644 --- a/modbus/modbushelpers.h +++ b/modbus/modbushelpers.h @@ -37,7 +37,7 @@ class ModbusHelpers { public: static float convertRegisterToFloat(const quint16 *reg); - static void convertFloatToRegister(QVector ®, float value); + static QVector convertFloatToRegister(float value); }; #endif diff --git a/modbus/modbustcpmaster.cpp b/modbus/modbustcpmaster.cpp index 22b035e..1022204 100644 --- a/modbus/modbustcpmaster.cpp +++ b/modbus/modbustcpmaster.cpp @@ -1,6 +1,6 @@ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * -* Copyright 2013 - 2020, nymea GmbH +* Copyright 2013 - 2021, nymea GmbH * Contact: contact@nymea.io * * This file is part of nymea. @@ -63,7 +63,7 @@ ModbusTCPMaster::~ModbusTCPMaster() } bool ModbusTCPMaster::connectDevice() { - // TCP connction to target device + // TCP connection to target device qCDebug(dcModbusTCP()) << "Setting up TCP connecion"; if (!m_modbusTcpClient) @@ -87,6 +87,11 @@ void ModbusTCPMaster::setTimeout(int timeout) m_modbusTcpClient->setTimeout(timeout); } +QString ModbusTCPMaster::errorString() const +{ + return m_modbusTcpClient->errorString(); +} + uint ModbusTCPMaster::port() { return m_modbusTcpClient->connectionParameter(QModbusDevice::NetworkPortParameter).toUInt(); diff --git a/modbus/modbustcpmaster.h b/modbus/modbustcpmaster.h index 977be48..ac604fe 100644 --- a/modbus/modbustcpmaster.h +++ b/modbus/modbustcpmaster.h @@ -1,6 +1,6 @@ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * -* Copyright 2013 - 2020, nymea GmbH +* Copyright 2013 - 2021, nymea GmbH * Contact: contact@nymea.io * * This file is part of nymea. @@ -37,7 +37,7 @@ #include #include -Q_DECLARE_LOGGING_CATEGORY(dcModbus) +Q_DECLARE_LOGGING_CATEGORY(dcModbusTcp) class ModbusTCPMaster : public QObject { @@ -51,6 +51,8 @@ public: void setNumberOfRetries(int number); void setTimeout(int timeout); + QString errorString() const; + QUuid readCoil(uint slaveAddress, uint registerAddress, uint size = 1); QUuid readDiscreteInput(uint slaveAddress, uint registerAddress, uint size = 1); QUuid readInputRegister(uint slaveAddress, uint registerAddress, uint size = 1);