PCE: update settings handling, update method and connection behavior

This commit is contained in:
Simon Stürz 2024-10-01 15:24:49 +02:00
parent 816c132c46
commit 323019fc05
6 changed files with 126 additions and 63 deletions

View File

@ -345,7 +345,7 @@
"registerType": "holdingRegister", "registerType": "holdingRegister",
"description": "LED brightness", "description": "LED brightness",
"unit": "%", "unit": "%",
"access": "WO" "access": "RW"
}, },
{ {
"id": "digitalInputMode", "id": "digitalInputMode",
@ -355,7 +355,7 @@
"registerType": "holdingRegister", "registerType": "holdingRegister",
"description": "Digital input mode", "description": "Digital input mode",
"enum": "DigitalInputMode", "enum": "DigitalInputMode",
"access": "WR" "access": "RW"
} }
] ]
} }

View File

@ -164,6 +164,9 @@ void IntegrationPluginPcElectric::thingRemoved(Thing *thing)
connection->deleteLater(); connection->deleteLater();
} }
if (m_initialUpdate.contains(thing))
m_initialUpdate.remove(thing);
// Unregister related hardware resources // Unregister related hardware resources
if (m_monitors.contains(thing)) if (m_monitors.contains(thing))
hardwareManager()->networkDeviceDiscovery()->unregisterMonitor(m_monitors.take(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); PceWallbox *connection = new PceWallbox(monitor->networkDeviceInfo().address(), 502, 1, this);
connect(info, &ThingSetupInfo::aborted, connection, &PceWallbox::deleteLater); connect(info, &ThingSetupInfo::aborted, connection, &PceWallbox::deleteLater);
if (monitor->networkDeviceInfo().isComplete())
connection->modbusTcpMaster()->setHostAddress(monitor->networkDeviceInfo().address());
// Monitor reachability // Monitor reachability
connect(monitor, &NetworkDeviceMonitor::reachableChanged, thing, [=](bool reachable){ connect(monitor, &NetworkDeviceMonitor::reachableChanged, thing, [=](bool reachable){
if (!thing->setupComplete()) if (!thing->setupComplete())
@ -302,12 +308,13 @@ void IntegrationPluginPcElectric::setupConnection(ThingSetupInfo *info)
}); });
// Connection reachability // 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; qCInfo(dcPcElectric()) << "Reachable changed to" << reachable << "for" << thing;
m_initialUpdate[thing] = true;
thing->setStateValue("connected", reachable); 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()) << "Update finished for" << thing;
qCDebug(dcPcElectric()) << connection; qCDebug(dcPcElectric()) << connection;
if (!connection->phaseAutoSwitch()) { if (!connection->phaseAutoSwitch()) {
@ -345,51 +352,61 @@ void IntegrationPluginPcElectric::setupConnection(ThingSetupInfo *info)
thing->setStateValue(ev11ErrorStateTypeId, "Kein Fehler aktiv"); thing->setStateValue(ev11ErrorStateTypeId, "Kein Fehler aktiv");
break; break;
case EV11ModbusTcpConnection::ErrorOverheating: case EV11ModbusTcpConnection::ErrorOverheating:
thing->setStateValue(ev11ErrorStateTypeId, "Übertemperatur. Ladevorgang wird automatisch fortgesetzt."); thing->setStateValue(ev11ErrorStateTypeId, "1: Übertemperatur. Ladevorgang wird automatisch fortgesetzt.");
break; break;
case EV11ModbusTcpConnection::ErrorDCFaultCurrent: case EV11ModbusTcpConnection::ErrorDCFaultCurrent:
thing->setStateValue(ev11ErrorStateTypeId, "DC Fehlerstromsensor ausgelöst."); thing->setStateValue(ev11ErrorStateTypeId, "2: DC Fehlerstromsensor ausgelöst.");
break; break;
case EV11ModbusTcpConnection::ErrorChargingWithVentilation: case EV11ModbusTcpConnection::ErrorChargingWithVentilation:
thing->setStateValue(ev11ErrorStateTypeId, "Ladeanforderung mit Belüftung."); thing->setStateValue(ev11ErrorStateTypeId, "3: Ladeanforderung mit Belüftung.");
break; break;
case EV11ModbusTcpConnection::ErrorCPErrorEF: case EV11ModbusTcpConnection::ErrorCPErrorEF:
thing->setStateValue(ev11ErrorStateTypeId, "CP Signal, Fehlercode E oder F."); thing->setStateValue(ev11ErrorStateTypeId, "4: CP Signal, Fehlercode E oder F.");
break; break;
case EV11ModbusTcpConnection::ErrorCPErrorBypass: case EV11ModbusTcpConnection::ErrorCPErrorBypass:
thing->setStateValue(ev11ErrorStateTypeId, "CP Signal, bypass."); thing->setStateValue(ev11ErrorStateTypeId, "5: CP Signal, bypass.");
break; break;
case EV11ModbusTcpConnection::ErrorCPErrorDiodFault: case EV11ModbusTcpConnection::ErrorCPErrorDiodFault:
thing->setStateValue(ev11ErrorStateTypeId, "CP Signal, Diode defekt."); thing->setStateValue(ev11ErrorStateTypeId, "6: CP Signal, Diode defekt.");
break; break;
case EV11ModbusTcpConnection::ErrorDCFaultCurrentCalibrating: case EV11ModbusTcpConnection::ErrorDCFaultCurrentCalibrating:
thing->setStateValue(ev11ErrorStateTypeId, "DC Fehlerstromsensor, Kalibrirung."); thing->setStateValue(ev11ErrorStateTypeId, "7: DC Fehlerstromsensor, Kalibrirung.");
break; break;
case EV11ModbusTcpConnection::ErrorDCFaultCurrentCommunication: case EV11ModbusTcpConnection::ErrorDCFaultCurrentCommunication:
thing->setStateValue(ev11ErrorStateTypeId, "DC Fehlerstromsensor, Kommunikationsfehler."); thing->setStateValue(ev11ErrorStateTypeId, "8: DC Fehlerstromsensor, Kommunikationsfehler.");
break; break;
case EV11ModbusTcpConnection::ErrorDCFaultCurrentError: case EV11ModbusTcpConnection::ErrorDCFaultCurrentError:
thing->setStateValue(ev11ErrorStateTypeId, "DC Fehlerstromsensor, Fehler."); thing->setStateValue(ev11ErrorStateTypeId, "9: DC Fehlerstromsensor, Fehler.");
break; break;
} }
switch (connection->digitalInputMode()) { if (m_initialUpdate.value(thing)) {
case EV11ModbusTcpConnection::DigitalInputModeEnableCharging:
thing->setSettingValue(ev11SettingsDigitalInputModeParamTypeId, "Charging allowed"); m_initialUpdate[thing] = false;
break; qCDebug(dcPcElectric()) << "Updating initial settings after connecting...";
case EV11ModbusTcpConnection::DigitalInputModeEnableChargingInverted:
thing->setSettingValue(ev11SettingsDigitalInputModeParamTypeId, "Charging allowed inverted"); thing->setSettingValue(ev11SettingsLedBrightnessParamTypeId, connection->ledBrightness());
break;
case EV11ModbusTcpConnection::DigitalInputModePwmS0Enabled: switch (connection->digitalInputMode()) {
thing->setSettingValue(ev11SettingsDigitalInputModeParamTypeId, "PWM and S0 signaling"); case EV11ModbusTcpConnection::DigitalInputModeEnableCharging:
break; 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 &paramTypeId, const QVariant &value){ connect(thing, &Thing::settingChanged, connection, [thing, connection](const ParamTypeId &paramTypeId, const QVariant &value){
if (paramTypeId == ev11SettingsLedBrightnessParamTypeId) { if (paramTypeId == ev11SettingsLedBrightnessParamTypeId) {
quint16 percentage = value.toUInt(); quint16 percentage = value.toUInt();
qCDebug(dcPcElectric()) << "Set LED brightness" << percentage << "%";
qCDebug(dcPcElectric()) << "Setting LED brightness to" << percentage << "%";
QueuedModbusReply *reply = connection->setLedBrightness(percentage); QueuedModbusReply *reply = connection->setLedBrightness(percentage);
connect(reply, &QueuedModbusReply::finished, thing, [reply, percentage](){ connect(reply, &QueuedModbusReply::finished, thing, [reply, percentage](){
if (reply->error() != QModbusDevice::NoError) { if (reply->error() != QModbusDevice::NoError) {
@ -401,13 +418,18 @@ void IntegrationPluginPcElectric::setupConnection(ThingSetupInfo *info)
}); });
} else if (paramTypeId == ev11SettingsDigitalInputModeParamTypeId) { } else if (paramTypeId == ev11SettingsDigitalInputModeParamTypeId) {
QString mode = value.toString(); QString mode = value.toString();
qCDebug(dcPcElectric()) << "Set Digital input mode" << mode; qCDebug(dcPcElectric()) << "Setting Digital input mode to" << mode;
EV11ModbusTcpConnection::DigitalInputMode modeValue = EV11ModbusTcpConnection::DigitalInputModeEnableCharging;
if (mode == "Charging allowed inverted") { EV11ModbusTcpConnection::DigitalInputMode modeValue;
if (mode == "Charging allowed") {
modeValue = EV11ModbusTcpConnection::DigitalInputModeEnableCharging;
} else if (mode == "Charging allowed inverted") {
modeValue = EV11ModbusTcpConnection::DigitalInputModeEnableChargingInverted; modeValue = EV11ModbusTcpConnection::DigitalInputModeEnableChargingInverted;
} else if (mode == "PWM and S0 signaling") { } else if (mode == "PWM and S0 signaling") {
modeValue = EV11ModbusTcpConnection::DigitalInputModePwmS0Enabled; modeValue = EV11ModbusTcpConnection::DigitalInputModePwmS0Enabled;
} else {
qCWarning(dcPcElectric()) << "Unknown mode value" << mode;
return;
} }
QueuedModbusReply *reply = connection->setDigitalInputMode(modeValue); QueuedModbusReply *reply = connection->setDigitalInputMode(modeValue);

View File

@ -61,6 +61,7 @@ private:
PluginTimer *m_refreshTimer = nullptr; PluginTimer *m_refreshTimer = nullptr;
QHash<Thing *, PceWallbox *> m_connections; QHash<Thing *, PceWallbox *> m_connections;
QHash<Thing *, NetworkDeviceMonitor *> m_monitors; QHash<Thing *, NetworkDeviceMonitor *> m_monitors;
QHash<Thing *, bool> m_initialUpdate;
void setupConnection(ThingSetupInfo *info); void setupConnection(ThingSetupInfo *info);

View File

@ -41,7 +41,7 @@
"minValue": 0, "minValue": 0,
"maxValue": 100, "maxValue": 100,
"unit": "Percentage", "unit": "Percentage",
"defaultValue": 50 "defaultValue": 100
}, },
{ {
"id": "930e0bf9-0038-436d-9eae-5c0f1cb28825", "id": "930e0bf9-0038-436d-9eae-5c0f1cb28825",
@ -49,7 +49,11 @@
"displayName": "Digital input mode", "displayName": "Digital input mode",
"type": "QString", "type": "QString",
"defaultValue": "Charging allowed", "defaultValue": "Charging allowed",
"allowedValues": ["Charging allowed", "Charging allowed inverted", "PWM and S0 signaling"] "allowedValues": [
"Charging allowed",
"Charging allowed inverted",
"PWM and S0 signaling"
]
} }
], ],
"stateTypes": [ "stateTypes": [

View File

@ -94,8 +94,7 @@ bool PceWallbox::update()
m_currentReply = nullptr; m_currentReply = nullptr;
if (reply->error() != QModbusDevice::NoError) { if (reply->error() != QModbusDevice::NoError) {
emit updateFinished(); QTimer::singleShot(0, this, &PceWallbox::sendNextRequest);
sendNextRequest();
return; return;
} }
@ -103,40 +102,76 @@ bool PceWallbox::update()
const QVector<quint16> blockValues = unit.values(); const QVector<quint16> blockValues = unit.values();
processBlockStatusRegisterValues(blockValues); processBlockStatusRegisterValues(blockValues);
emit updateFinished(); QTimer::singleShot(0, this, &PceWallbox::sendNextRequest);
sendNextRequest();
}); });
enqueueRequest(reply); enqueueRequest(reply);
// Digital input
bool digitalInputAlreadyQueued = false;
foreach (QueuedModbusReply *r, m_queue) { foreach (QueuedModbusReply *r, m_queue) {
if (r->dataUnit().startAddress() == digitalInputModeDataUnit().startAddress()) { if (r->dataUnit().startAddress() == digitalInputModeDataUnit().startAddress()) {
return true; digitalInputAlreadyQueued = true;
break;
} }
} }
reply = new QueuedModbusReply(QueuedModbusReply::RequestTypeRead, digitalInputModeDataUnit(), this); if (!digitalInputAlreadyQueued) {
connect(reply, &QueuedModbusReply::finished, reply, &QueuedModbusReply::deleteLater); reply = new QueuedModbusReply(QueuedModbusReply::RequestTypeRead, digitalInputModeDataUnit(), this);
connect(reply, &QueuedModbusReply::finished, this, [this, reply](){ connect(reply, &QueuedModbusReply::finished, reply, &QueuedModbusReply::deleteLater);
connect(reply, &QueuedModbusReply::finished, this, [this, reply](){
if (m_currentReply == reply) if (m_currentReply == reply)
m_currentReply = nullptr; m_currentReply = nullptr;
if (reply->error() != QModbusDevice::NoError) { if (reply->error() != QModbusDevice::NoError) {
emit updateFinished(); QTimer::singleShot(0, this, &PceWallbox::sendNextRequest);
sendNextRequest(); return;
return; }
const QModbusDataUnit unit = reply->reply()->result();
const QVector<quint16> 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(); if (!ledBrightnessAlreadyQueued) {
const QVector<quint16> values = unit.values(); reply = new QueuedModbusReply(QueuedModbusReply::RequestTypeRead, ledBrightnessDataUnit(), this);
processDigitalInputModeRegisterValues(values); connect(reply, &QueuedModbusReply::finished, reply, &QueuedModbusReply::deleteLater);
connect(reply, &QueuedModbusReply::finished, this, [this, reply](){
emit updateFinished(); if (m_currentReply == reply)
sendNextRequest(); 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<quint16> values = unit.values();
processLedBrightnessRegisterValues(values);
emit updateFinished();
QTimer::singleShot(0, this, &PceWallbox::sendNextRequest);
});
enqueueRequest(reply);
}
return true; return true;
} }
@ -152,7 +187,7 @@ QueuedModbusReply *PceWallbox::setChargingCurrent(quint16 chargingCurrent)
if (m_currentReply == reply) if (m_currentReply == reply)
m_currentReply = nullptr; m_currentReply = nullptr;
sendNextRequest(); QTimer::singleShot(0, this, &PceWallbox::sendNextRequest);
return; return;
}); });
@ -172,7 +207,7 @@ QueuedModbusReply *PceWallbox::setLedBrightness(quint16 percentage)
if (m_currentReply == reply) if (m_currentReply == reply)
m_currentReply = nullptr; m_currentReply = nullptr;
sendNextRequest(); QTimer::singleShot(0, this, &PceWallbox::sendNextRequest);
return; return;
}); });
@ -192,7 +227,7 @@ QueuedModbusReply *PceWallbox::setDigitalInputMode(DigitalInputMode digitalInput
if (m_currentReply == reply) if (m_currentReply == reply)
m_currentReply = nullptr; m_currentReply = nullptr;
sendNextRequest(); QTimer::singleShot(0, this, &PceWallbox::sendNextRequest);
return; return;
}); });
@ -228,7 +263,6 @@ void PceWallbox::sendHeartbeat()
QueuedModbusReply *reply = new QueuedModbusReply(QueuedModbusReply::RequestTypeWrite, setHeartbeatDataUnit(m_heartbeat++), this); QueuedModbusReply *reply = new QueuedModbusReply(QueuedModbusReply::RequestTypeWrite, setHeartbeatDataUnit(m_heartbeat++), this);
connect(reply, &QueuedModbusReply::finished, reply, &QueuedModbusReply::deleteLater); connect(reply, &QueuedModbusReply::finished, reply, &QueuedModbusReply::deleteLater);
connect(reply, &QueuedModbusReply::finished, this, [this, reply](){ connect(reply, &QueuedModbusReply::finished, this, [this, reply](){
if (m_currentReply == reply) if (m_currentReply == reply)
m_currentReply = nullptr; m_currentReply = nullptr;
@ -239,7 +273,7 @@ void PceWallbox::sendHeartbeat()
qCDebug(dcPcElectric()) << "Successfully sent heartbeat to" << m_modbusTcpMaster->hostAddress().toString(); qCDebug(dcPcElectric()) << "Successfully sent heartbeat to" << m_modbusTcpMaster->hostAddress().toString();
} }
sendNextRequest(); QTimer::singleShot(0, this, &PceWallbox::sendNextRequest);
return; return;
}); });
@ -280,13 +314,13 @@ void PceWallbox::sendNextRequest()
if (!m_currentReply->reply()) { if (!m_currentReply->reply()) {
qCWarning(dcPcElectric()) << "Error occurred while sending" << m_currentReply->requestType() qCWarning(dcPcElectric()) << "Error occurred while sending" << m_currentReply->requestType()
<< ModbusDataUtils::registerTypeToString(m_currentReply->dataUnit().registerType()) << ModbusDataUtils::registerTypeToString(m_currentReply->dataUnit().registerType())
<< "register:" << m_currentReply->dataUnit().startAddress() << "register:" << m_currentReply->dataUnit().startAddress()
<< "length:" << m_currentReply->dataUnit().valueCount() << "length:" << m_currentReply->dataUnit().valueCount()
<< "to" << m_modbusTcpMaster->hostAddress().toString() << m_modbusTcpMaster->errorString(); << "to" << m_modbusTcpMaster->hostAddress().toString() << m_modbusTcpMaster->errorString();
m_currentReply->deleteLater(); m_currentReply->deleteLater();
m_currentReply = nullptr; m_currentReply = nullptr;
sendNextRequest(); QTimer::singleShot(0, this, &PceWallbox::sendNextRequest);
return; return;
} }
@ -294,7 +328,7 @@ void PceWallbox::sendNextRequest()
qCWarning(dcPcElectric()) << "Reply immediatly finished"; qCWarning(dcPcElectric()) << "Reply immediatly finished";
m_currentReply->deleteLater(); m_currentReply->deleteLater();
m_currentReply = nullptr; m_currentReply = nullptr;
sendNextRequest(); QTimer::singleShot(0, this, &PceWallbox::sendNextRequest);
return; return;
} }
} }
@ -307,7 +341,7 @@ void PceWallbox::enqueueRequest(QueuedModbusReply *reply, bool prepend)
m_queue.enqueue(reply); m_queue.enqueue(reply);
} }
sendNextRequest(); QTimer::singleShot(0, this, &PceWallbox::sendNextRequest);
} }
void PceWallbox::cleanupQueue() void PceWallbox::cleanupQueue()

View File

@ -53,6 +53,8 @@ public:
QueuedModbusReply *setDigitalInputMode(DigitalInputMode digitalInputMode); QueuedModbusReply *setDigitalInputMode(DigitalInputMode digitalInputMode);
// Note: the modbus implementation of the wallbox gets stuck if a Modbus request has been sent // 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 // 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 // fixes the broken communication afterwards. This method waits for the current request before closing the
@ -62,6 +64,7 @@ public:
private slots: private slots:
void sendHeartbeat(); void sendHeartbeat();
void sendNextRequest();
private: private:
QTimer m_timer; QTimer m_timer;
@ -70,7 +73,6 @@ private:
QQueue<QueuedModbusReply *> m_queue; QQueue<QueuedModbusReply *> m_queue;
bool m_aboutToDelete = false; bool m_aboutToDelete = false;
void sendNextRequest();
void enqueueRequest(QueuedModbusReply *reply, bool prepend = false); void enqueueRequest(QueuedModbusReply *reply, bool prepend = false);
void cleanupQueue(); void cleanupQueue();