diff --git a/pcelectric/EV11.3-registers.json b/pcelectric/EV11.3-registers.json index d4c566e..936b0d3 100644 --- a/pcelectric/EV11.3-registers.json +++ b/pcelectric/EV11.3-registers.json @@ -345,7 +345,7 @@ "registerType": "holdingRegister", "description": "LED brightness", "unit": "%", - "access": "WO" + "access": "RW" }, { "id": "digitalInputMode", @@ -355,7 +355,7 @@ "registerType": "holdingRegister", "description": "Digital input mode", "enum": "DigitalInputMode", - "access": "WR" + "access": "RW" } ] } diff --git a/pcelectric/integrationpluginpcelectric.cpp b/pcelectric/integrationpluginpcelectric.cpp index 5e019dc..e86d2a1 100644 --- a/pcelectric/integrationpluginpcelectric.cpp +++ b/pcelectric/integrationpluginpcelectric.cpp @@ -164,6 +164,9 @@ void IntegrationPluginPcElectric::thingRemoved(Thing *thing) connection->deleteLater(); } + if (m_initialUpdate.contains(thing)) + m_initialUpdate.remove(thing); + // Unregister related hardware resources if (m_monitors.contains(thing)) hardwareManager()->networkDeviceDiscovery()->unregisterMonitor(m_monitors.take(thing)); @@ -285,6 +288,9 @@ void IntegrationPluginPcElectric::setupConnection(ThingSetupInfo *info) PceWallbox *connection = new PceWallbox(monitor->networkDeviceInfo().address(), 502, 1, this); connect(info, &ThingSetupInfo::aborted, connection, &PceWallbox::deleteLater); + if (monitor->networkDeviceInfo().isComplete()) + connection->modbusTcpMaster()->setHostAddress(monitor->networkDeviceInfo().address()); + // Monitor reachability connect(monitor, &NetworkDeviceMonitor::reachableChanged, thing, [=](bool reachable){ if (!thing->setupComplete()) @@ -302,12 +308,13 @@ void IntegrationPluginPcElectric::setupConnection(ThingSetupInfo *info) }); // Connection reachability - connect(connection, &PceWallbox::reachableChanged, thing, [thing](bool reachable){ + connect(connection, &PceWallbox::reachableChanged, thing, [this, thing](bool reachable){ qCInfo(dcPcElectric()) << "Reachable changed to" << reachable << "for" << thing; + m_initialUpdate[thing] = true; thing->setStateValue("connected", reachable); }); - connect(connection, &PceWallbox::updateFinished, thing, [thing, connection](){ + connect(connection, &PceWallbox::updateFinished, thing, [this, thing, connection](){ qCDebug(dcPcElectric()) << "Update finished for" << thing; qCDebug(dcPcElectric()) << connection; if (!connection->phaseAutoSwitch()) { @@ -345,51 +352,61 @@ void IntegrationPluginPcElectric::setupConnection(ThingSetupInfo *info) thing->setStateValue(ev11ErrorStateTypeId, "Kein Fehler aktiv"); break; case EV11ModbusTcpConnection::ErrorOverheating: - thing->setStateValue(ev11ErrorStateTypeId, "Übertemperatur. Ladevorgang wird automatisch fortgesetzt."); + thing->setStateValue(ev11ErrorStateTypeId, "1: Übertemperatur. Ladevorgang wird automatisch fortgesetzt."); break; case EV11ModbusTcpConnection::ErrorDCFaultCurrent: - thing->setStateValue(ev11ErrorStateTypeId, "DC Fehlerstromsensor ausgelöst."); + thing->setStateValue(ev11ErrorStateTypeId, "2: DC Fehlerstromsensor ausgelöst."); break; case EV11ModbusTcpConnection::ErrorChargingWithVentilation: - thing->setStateValue(ev11ErrorStateTypeId, "Ladeanforderung mit Belüftung."); + thing->setStateValue(ev11ErrorStateTypeId, "3: Ladeanforderung mit Belüftung."); break; case EV11ModbusTcpConnection::ErrorCPErrorEF: - thing->setStateValue(ev11ErrorStateTypeId, "CP Signal, Fehlercode E oder F."); + thing->setStateValue(ev11ErrorStateTypeId, "4: CP Signal, Fehlercode E oder F."); break; case EV11ModbusTcpConnection::ErrorCPErrorBypass: - thing->setStateValue(ev11ErrorStateTypeId, "CP Signal, bypass."); + thing->setStateValue(ev11ErrorStateTypeId, "5: CP Signal, bypass."); break; case EV11ModbusTcpConnection::ErrorCPErrorDiodFault: - thing->setStateValue(ev11ErrorStateTypeId, "CP Signal, Diode defekt."); + thing->setStateValue(ev11ErrorStateTypeId, "6: CP Signal, Diode defekt."); break; case EV11ModbusTcpConnection::ErrorDCFaultCurrentCalibrating: - thing->setStateValue(ev11ErrorStateTypeId, "DC Fehlerstromsensor, Kalibrirung."); + thing->setStateValue(ev11ErrorStateTypeId, "7: DC Fehlerstromsensor, Kalibrirung."); break; case EV11ModbusTcpConnection::ErrorDCFaultCurrentCommunication: - thing->setStateValue(ev11ErrorStateTypeId, "DC Fehlerstromsensor, Kommunikationsfehler."); + thing->setStateValue(ev11ErrorStateTypeId, "8: DC Fehlerstromsensor, Kommunikationsfehler."); break; case EV11ModbusTcpConnection::ErrorDCFaultCurrentError: - thing->setStateValue(ev11ErrorStateTypeId, "DC Fehlerstromsensor, Fehler."); + thing->setStateValue(ev11ErrorStateTypeId, "9: DC Fehlerstromsensor, Fehler."); break; } - switch (connection->digitalInputMode()) { - case EV11ModbusTcpConnection::DigitalInputModeEnableCharging: - thing->setSettingValue(ev11SettingsDigitalInputModeParamTypeId, "Charging allowed"); - break; - case EV11ModbusTcpConnection::DigitalInputModeEnableChargingInverted: - thing->setSettingValue(ev11SettingsDigitalInputModeParamTypeId, "Charging allowed inverted"); - break; - case EV11ModbusTcpConnection::DigitalInputModePwmS0Enabled: - thing->setSettingValue(ev11SettingsDigitalInputModeParamTypeId, "PWM and S0 signaling"); - break; + if (m_initialUpdate.value(thing)) { + + m_initialUpdate[thing] = false; + qCDebug(dcPcElectric()) << "Updating initial settings after connecting..."; + + thing->setSettingValue(ev11SettingsLedBrightnessParamTypeId, connection->ledBrightness()); + + switch (connection->digitalInputMode()) { + case EV11ModbusTcpConnection::DigitalInputModeEnableCharging: + thing->setSettingValue(ev11SettingsDigitalInputModeParamTypeId, "Charging allowed"); + break; + case EV11ModbusTcpConnection::DigitalInputModeEnableChargingInverted: + thing->setSettingValue(ev11SettingsDigitalInputModeParamTypeId, "Charging allowed inverted"); + break; + case EV11ModbusTcpConnection::DigitalInputModePwmS0Enabled: + thing->setSettingValue(ev11SettingsDigitalInputModeParamTypeId, "PWM and S0 signaling"); + break; + } } }); connect(thing, &Thing::settingChanged, connection, [thing, connection](const ParamTypeId ¶mTypeId, const QVariant &value){ + if (paramTypeId == ev11SettingsLedBrightnessParamTypeId) { quint16 percentage = value.toUInt(); - qCDebug(dcPcElectric()) << "Set LED brightness" << percentage << "%"; + + qCDebug(dcPcElectric()) << "Setting LED brightness to" << percentage << "%"; QueuedModbusReply *reply = connection->setLedBrightness(percentage); connect(reply, &QueuedModbusReply::finished, thing, [reply, percentage](){ if (reply->error() != QModbusDevice::NoError) { @@ -401,13 +418,18 @@ void IntegrationPluginPcElectric::setupConnection(ThingSetupInfo *info) }); } else if (paramTypeId == ev11SettingsDigitalInputModeParamTypeId) { QString mode = value.toString(); - qCDebug(dcPcElectric()) << "Set Digital input mode" << mode; - EV11ModbusTcpConnection::DigitalInputMode modeValue = EV11ModbusTcpConnection::DigitalInputModeEnableCharging; + qCDebug(dcPcElectric()) << "Setting Digital input mode to" << mode; - if (mode == "Charging allowed inverted") { + EV11ModbusTcpConnection::DigitalInputMode modeValue; + if (mode == "Charging allowed") { + modeValue = EV11ModbusTcpConnection::DigitalInputModeEnableCharging; + } else if (mode == "Charging allowed inverted") { modeValue = EV11ModbusTcpConnection::DigitalInputModeEnableChargingInverted; } else if (mode == "PWM and S0 signaling") { modeValue = EV11ModbusTcpConnection::DigitalInputModePwmS0Enabled; + } else { + qCWarning(dcPcElectric()) << "Unknown mode value" << mode; + return; } QueuedModbusReply *reply = connection->setDigitalInputMode(modeValue); diff --git a/pcelectric/integrationpluginpcelectric.h b/pcelectric/integrationpluginpcelectric.h index 9fb1519..036989b 100644 --- a/pcelectric/integrationpluginpcelectric.h +++ b/pcelectric/integrationpluginpcelectric.h @@ -61,6 +61,7 @@ private: PluginTimer *m_refreshTimer = nullptr; QHash m_connections; QHash m_monitors; + QHash m_initialUpdate; void setupConnection(ThingSetupInfo *info); diff --git a/pcelectric/integrationpluginpcelectric.json b/pcelectric/integrationpluginpcelectric.json index e646b41..82247fe 100644 --- a/pcelectric/integrationpluginpcelectric.json +++ b/pcelectric/integrationpluginpcelectric.json @@ -41,7 +41,7 @@ "minValue": 0, "maxValue": 100, "unit": "Percentage", - "defaultValue": 50 + "defaultValue": 100 }, { "id": "930e0bf9-0038-436d-9eae-5c0f1cb28825", @@ -49,7 +49,11 @@ "displayName": "Digital input mode", "type": "QString", "defaultValue": "Charging allowed", - "allowedValues": ["Charging allowed", "Charging allowed inverted", "PWM and S0 signaling"] + "allowedValues": [ + "Charging allowed", + "Charging allowed inverted", + "PWM and S0 signaling" + ] } ], "stateTypes": [ diff --git a/pcelectric/pcewallbox.cpp b/pcelectric/pcewallbox.cpp index 25ef7f2..0ea2f01 100644 --- a/pcelectric/pcewallbox.cpp +++ b/pcelectric/pcewallbox.cpp @@ -94,8 +94,7 @@ bool PceWallbox::update() m_currentReply = nullptr; if (reply->error() != QModbusDevice::NoError) { - emit updateFinished(); - sendNextRequest(); + QTimer::singleShot(0, this, &PceWallbox::sendNextRequest); return; } @@ -103,40 +102,76 @@ bool PceWallbox::update() const QVector blockValues = unit.values(); processBlockStatusRegisterValues(blockValues); - emit updateFinished(); - sendNextRequest(); + QTimer::singleShot(0, this, &PceWallbox::sendNextRequest); }); enqueueRequest(reply); + // Digital input + bool digitalInputAlreadyQueued = false; foreach (QueuedModbusReply *r, m_queue) { if (r->dataUnit().startAddress() == digitalInputModeDataUnit().startAddress()) { - return true; + digitalInputAlreadyQueued = true; + break; } } - reply = new QueuedModbusReply(QueuedModbusReply::RequestTypeRead, digitalInputModeDataUnit(), this); - connect(reply, &QueuedModbusReply::finished, reply, &QueuedModbusReply::deleteLater); - connect(reply, &QueuedModbusReply::finished, this, [this, reply](){ + if (!digitalInputAlreadyQueued) { + reply = new QueuedModbusReply(QueuedModbusReply::RequestTypeRead, digitalInputModeDataUnit(), this); + connect(reply, &QueuedModbusReply::finished, reply, &QueuedModbusReply::deleteLater); + connect(reply, &QueuedModbusReply::finished, this, [this, reply](){ - if (m_currentReply == reply) - m_currentReply = nullptr; + if (m_currentReply == reply) + m_currentReply = nullptr; - if (reply->error() != QModbusDevice::NoError) { - emit updateFinished(); - sendNextRequest(); - return; + if (reply->error() != QModbusDevice::NoError) { + QTimer::singleShot(0, this, &PceWallbox::sendNextRequest); + return; + } + + const QModbusDataUnit unit = reply->reply()->result(); + const QVector values = unit.values(); + processDigitalInputModeRegisterValues(values); + + QTimer::singleShot(0, this, &PceWallbox::sendNextRequest); + }); + + enqueueRequest(reply); + } + + + // Led brightness + bool ledBrightnessAlreadyQueued = false; + foreach (QueuedModbusReply *r, m_queue) { + if (r->dataUnit().startAddress() == ledBrightnessDataUnit().startAddress()) { + ledBrightnessAlreadyQueued = true; + break; } + } - const QModbusDataUnit unit = reply->reply()->result(); - const QVector values = unit.values(); - processDigitalInputModeRegisterValues(values); + if (!ledBrightnessAlreadyQueued) { + reply = new QueuedModbusReply(QueuedModbusReply::RequestTypeRead, ledBrightnessDataUnit(), this); + connect(reply, &QueuedModbusReply::finished, reply, &QueuedModbusReply::deleteLater); + connect(reply, &QueuedModbusReply::finished, this, [this, reply](){ - emit updateFinished(); - sendNextRequest(); - }); + if (m_currentReply == reply) + m_currentReply = nullptr; - enqueueRequest(reply); + if (reply->error() != QModbusDevice::NoError) { + QTimer::singleShot(0, this, &PceWallbox::sendNextRequest); + return; + } + + const QModbusDataUnit unit = reply->reply()->result(); + const QVector values = unit.values(); + processLedBrightnessRegisterValues(values); + + emit updateFinished(); + QTimer::singleShot(0, this, &PceWallbox::sendNextRequest); + }); + + enqueueRequest(reply); + } return true; } @@ -152,7 +187,7 @@ QueuedModbusReply *PceWallbox::setChargingCurrent(quint16 chargingCurrent) if (m_currentReply == reply) m_currentReply = nullptr; - sendNextRequest(); + QTimer::singleShot(0, this, &PceWallbox::sendNextRequest); return; }); @@ -172,7 +207,7 @@ QueuedModbusReply *PceWallbox::setLedBrightness(quint16 percentage) if (m_currentReply == reply) m_currentReply = nullptr; - sendNextRequest(); + QTimer::singleShot(0, this, &PceWallbox::sendNextRequest); return; }); @@ -192,7 +227,7 @@ QueuedModbusReply *PceWallbox::setDigitalInputMode(DigitalInputMode digitalInput if (m_currentReply == reply) m_currentReply = nullptr; - sendNextRequest(); + QTimer::singleShot(0, this, &PceWallbox::sendNextRequest); return; }); @@ -228,7 +263,6 @@ void PceWallbox::sendHeartbeat() QueuedModbusReply *reply = new QueuedModbusReply(QueuedModbusReply::RequestTypeWrite, setHeartbeatDataUnit(m_heartbeat++), this); connect(reply, &QueuedModbusReply::finished, reply, &QueuedModbusReply::deleteLater); - connect(reply, &QueuedModbusReply::finished, this, [this, reply](){ if (m_currentReply == reply) m_currentReply = nullptr; @@ -239,7 +273,7 @@ void PceWallbox::sendHeartbeat() qCDebug(dcPcElectric()) << "Successfully sent heartbeat to" << m_modbusTcpMaster->hostAddress().toString(); } - sendNextRequest(); + QTimer::singleShot(0, this, &PceWallbox::sendNextRequest); return; }); @@ -280,13 +314,13 @@ void PceWallbox::sendNextRequest() if (!m_currentReply->reply()) { qCWarning(dcPcElectric()) << "Error occurred while sending" << m_currentReply->requestType() - << ModbusDataUtils::registerTypeToString(m_currentReply->dataUnit().registerType()) - << "register:" << m_currentReply->dataUnit().startAddress() - << "length:" << m_currentReply->dataUnit().valueCount() - << "to" << m_modbusTcpMaster->hostAddress().toString() << m_modbusTcpMaster->errorString(); + << ModbusDataUtils::registerTypeToString(m_currentReply->dataUnit().registerType()) + << "register:" << m_currentReply->dataUnit().startAddress() + << "length:" << m_currentReply->dataUnit().valueCount() + << "to" << m_modbusTcpMaster->hostAddress().toString() << m_modbusTcpMaster->errorString(); m_currentReply->deleteLater(); m_currentReply = nullptr; - sendNextRequest(); + QTimer::singleShot(0, this, &PceWallbox::sendNextRequest); return; } @@ -294,7 +328,7 @@ void PceWallbox::sendNextRequest() qCWarning(dcPcElectric()) << "Reply immediatly finished"; m_currentReply->deleteLater(); m_currentReply = nullptr; - sendNextRequest(); + QTimer::singleShot(0, this, &PceWallbox::sendNextRequest); return; } } @@ -307,7 +341,7 @@ void PceWallbox::enqueueRequest(QueuedModbusReply *reply, bool prepend) m_queue.enqueue(reply); } - sendNextRequest(); + QTimer::singleShot(0, this, &PceWallbox::sendNextRequest); } void PceWallbox::cleanupQueue() diff --git a/pcelectric/pcewallbox.h b/pcelectric/pcewallbox.h index 3a7a150..6650cfd 100644 --- a/pcelectric/pcewallbox.h +++ b/pcelectric/pcewallbox.h @@ -53,6 +53,8 @@ public: QueuedModbusReply *setDigitalInputMode(DigitalInputMode digitalInputMode); + + // Note: the modbus implementation of the wallbox gets stuck if a Modbus request has been sent // and we disconnect the socket before the response has arrived. Only a reboot of the wallbox // fixes the broken communication afterwards. This method waits for the current request before closing the @@ -62,6 +64,7 @@ public: private slots: void sendHeartbeat(); + void sendNextRequest(); private: QTimer m_timer; @@ -70,7 +73,6 @@ private: QQueue m_queue; bool m_aboutToDelete = false; - void sendNextRequest(); void enqueueRequest(QueuedModbusReply *reply, bool prepend = false); void cleanupQueue();