PcElectric: Update action handling based on single register
Signed-off-by: Simon Stürz <simon.stuerz@nymea.io>
This commit is contained in:
parent
8d85307f46
commit
b6cdaeffa0
@ -306,7 +306,7 @@
|
||||
"registerType": "holdingRegister",
|
||||
"description": "Write charging current",
|
||||
"unit": "mA",
|
||||
"access": "WO"
|
||||
"access": "RW"
|
||||
},
|
||||
{
|
||||
"id": "chargingCurrentOffline",
|
||||
|
||||
@ -139,6 +139,7 @@ void IntegrationPluginPcElectric::setupThing(ThingSetupInfo *info)
|
||||
void IntegrationPluginPcElectric::postSetupThing(Thing *thing)
|
||||
{
|
||||
qCDebug(dcPcElectric()) << "Post setup thing" << thing->name();
|
||||
|
||||
if (!m_refreshTimer) {
|
||||
m_refreshTimer = hardwareManager()->pluginTimerManager()->registerTimer(1);
|
||||
connect(m_refreshTimer, &PluginTimer::timeout, this, [this] {
|
||||
@ -152,6 +153,14 @@ void IntegrationPluginPcElectric::postSetupThing(Thing *thing)
|
||||
qCDebug(dcPcElectric()) << "Starting refresh timer...";
|
||||
m_refreshTimer->start();
|
||||
}
|
||||
|
||||
|
||||
PceWallbox::ChargingCurrentState chargingCurrentState;
|
||||
chargingCurrentState.power = thing->stateValue(ev11PowerStateTypeId).toBool();
|
||||
chargingCurrentState.maxChargingCurrent = thing->stateValue(ev11MaxChargingCurrentStateTypeId).toDouble();
|
||||
chargingCurrentState.desiredPhaseCount = thing->stateValue(ev11DesiredPhaseCountStateTypeId).toDouble();
|
||||
qCDebug(dcPcElectric()) << "Initialize charging current state with cached values" << chargingCurrentState;
|
||||
m_chargingCurrentStateBuffer[thing] = chargingCurrentState;
|
||||
}
|
||||
|
||||
void IntegrationPluginPcElectric::thingRemoved(Thing *thing)
|
||||
@ -167,6 +176,9 @@ void IntegrationPluginPcElectric::thingRemoved(Thing *thing)
|
||||
if (m_initialUpdate.contains(thing))
|
||||
m_initialUpdate.remove(thing);
|
||||
|
||||
if (m_chargingCurrentStateBuffer.contains(thing))
|
||||
m_chargingCurrentStateBuffer.remove(thing);
|
||||
|
||||
// Unregister related hardware resources
|
||||
if (m_monitors.contains(thing))
|
||||
hardwareManager()->networkDeviceDiscovery()->unregisterMonitor(m_monitors.take(thing));
|
||||
@ -190,91 +202,81 @@ void IntegrationPluginPcElectric::executeAction(ThingActionInfo *info)
|
||||
}
|
||||
|
||||
if (info->action().actionTypeId() == ev11PowerActionTypeId) {
|
||||
bool power = info->action().paramValue(ev11PowerActionPowerParamTypeId).toBool();
|
||||
quint16 chargingCurrent = 0;
|
||||
if (power) {
|
||||
chargingCurrent = thing->stateValue(ev11MaxChargingCurrentStateTypeId).toUInt() * 1000;
|
||||
if (thing->stateValue(ev11DesiredPhaseCountStateTypeId).toUInt() == 3) {
|
||||
// If 3 phase charging is enabled, we set the first bit
|
||||
chargingCurrent |= static_cast<quint16>(1) << 15;
|
||||
}
|
||||
}
|
||||
|
||||
qCDebug(dcPcElectric()) << "Writing charging current register" << chargingCurrent << "mA";
|
||||
QueuedModbusReply *reply = connection->setChargingCurrent(chargingCurrent);
|
||||
connect(reply, &QueuedModbusReply::finished, info, [reply, info, thing, power, chargingCurrent](){
|
||||
bool power = info->action().paramValue(ev11PowerActionPowerParamTypeId).toBool();
|
||||
qCDebug(dcPcElectric()) << "Set charging enabled to" << power;
|
||||
|
||||
// Update buffer
|
||||
m_chargingCurrentStateBuffer[thing].power = power;
|
||||
|
||||
quint16 registerValue = PceWallbox::deriveRegisterFromStates(m_chargingCurrentStateBuffer.value(thing));
|
||||
qCDebug(dcPcElectric()) << "Writing charging current register" << registerValue;
|
||||
QueuedModbusReply *reply = connection->setChargingCurrent(registerValue);
|
||||
connect(reply, &QueuedModbusReply::finished, info, [reply, info, thing, power, registerValue](){
|
||||
if (reply->error() != QModbusDevice::NoError) {
|
||||
qCWarning(dcPcElectric()) << "Could not set power state to" << power << "(" << chargingCurrent << "mA)" << reply->errorString();
|
||||
qCWarning(dcPcElectric()) << "Could not set power state to" << power << "(" << registerValue << ")" << reply->errorString();
|
||||
info->finish(Thing::ThingErrorHardwareFailure);
|
||||
return;
|
||||
}
|
||||
|
||||
qCDebug(dcPcElectric()) << "Successfully set power state to" << power << "(" << chargingCurrent << "mA)";
|
||||
qCDebug(dcPcElectric()) << "Successfully set power state to" << power << "(" << registerValue << ")";
|
||||
thing->setStateValue(ev11PowerStateTypeId, power);
|
||||
info->finish(Thing::ThingErrorNoError);
|
||||
});
|
||||
|
||||
return;
|
||||
|
||||
} else if (info->action().actionTypeId() == ev11MaxChargingCurrentActionTypeId) {
|
||||
|
||||
uint desiredChargingCurrent = info->action().paramValue(ev11MaxChargingCurrentActionMaxChargingCurrentParamTypeId).toUInt();
|
||||
qCDebug(dcPcElectric()) << "Set max charging current to" << desiredChargingCurrent << "A";
|
||||
if (thing->stateValue(ev11PowerStateTypeId).toBool()) {
|
||||
// The charging is enabled, let's write the value to the wallbox
|
||||
quint16 finalChargingCurrent = static_cast<quint16>(desiredChargingCurrent * 1000);
|
||||
if (thing->stateValue(ev11DesiredPhaseCountStateTypeId).toUInt() == 3) {
|
||||
// If 3 phase charging is enabled, we set the first bit
|
||||
finalChargingCurrent |= static_cast<quint16>(1) << 15;
|
||||
|
||||
// Update buffer
|
||||
m_chargingCurrentStateBuffer[thing].maxChargingCurrent = desiredChargingCurrent;
|
||||
|
||||
quint16 registerValue = PceWallbox::deriveRegisterFromStates(m_chargingCurrentStateBuffer.value(thing));
|
||||
qCDebug(dcPcElectric()) << "Writing charging current register" << registerValue;
|
||||
QueuedModbusReply *reply = connection->setChargingCurrent(registerValue);
|
||||
connect(reply, &QueuedModbusReply::finished, info, [reply, info, thing, desiredChargingCurrent](){
|
||||
if (reply->error() != QModbusDevice::NoError) {
|
||||
qCWarning(dcPcElectric()) << "Could not set charging current to" << desiredChargingCurrent << reply->errorString();
|
||||
info->finish(Thing::ThingErrorHardwareFailure);
|
||||
return;
|
||||
}
|
||||
|
||||
qCDebug(dcPcElectric()) << "Writing charging current register" << finalChargingCurrent << "mA";
|
||||
QueuedModbusReply *reply = connection->setChargingCurrent(finalChargingCurrent);
|
||||
connect(reply, &QueuedModbusReply::finished, info, [reply, info, thing, desiredChargingCurrent](){
|
||||
if (reply->error() != QModbusDevice::NoError) {
|
||||
qCWarning(dcPcElectric()) << "Could not set charging current to" << desiredChargingCurrent << "mA" << reply->errorString();
|
||||
info->finish(Thing::ThingErrorHardwareFailure);
|
||||
return;
|
||||
}
|
||||
|
||||
qCDebug(dcPcElectric()) << "Successfully set charging current to" << desiredChargingCurrent << "mA";
|
||||
thing->setStateValue(ev11MaxChargingCurrentStateTypeId, desiredChargingCurrent);
|
||||
info->finish(Thing::ThingErrorNoError);
|
||||
});
|
||||
} else {
|
||||
// Save the value in the state, but do not send the value to the wallbox since the power state is reflected using the charging current...
|
||||
qCDebug(dcPcElectric()) << "Setting charging current to" << desiredChargingCurrent << "without synching to wallbox since the power state is false";
|
||||
qCDebug(dcPcElectric()) << "Successfully set charging current (" << desiredChargingCurrent << ")";
|
||||
thing->setStateValue(ev11MaxChargingCurrentStateTypeId, desiredChargingCurrent);
|
||||
info->finish(Thing::ThingErrorNoError);
|
||||
}
|
||||
return;
|
||||
} else if (info->action().actionTypeId() == ev11DesiredPhaseCountActionTypeId) {
|
||||
uint desiredPhaseCount = info->action().paramValue(ev11DesiredPhaseCountActionDesiredPhaseCountParamTypeId).toUInt();
|
||||
qCDebug(dcPcElectric()) << "Desried phase count changed" << desiredPhaseCount;
|
||||
thing->setStateValue(ev11DesiredPhaseCountStateTypeId, desiredPhaseCount);
|
||||
info->finish(Thing::ThingErrorNoError);
|
||||
});
|
||||
|
||||
// Update the max charging current according to the new desired phase count
|
||||
if (thing->stateValue(ev11PowerStateTypeId).toBool()) {
|
||||
uint chargingCurrent = thing->stateValue(ev11MaxChargingCurrentStateTypeId).toUInt();
|
||||
quint16 finalChargingCurrent = static_cast<quint16>(chargingCurrent * 1000);
|
||||
if (thing->stateValue(ev11DesiredPhaseCountStateTypeId).toUInt() == 3) {
|
||||
// If 3 phase charging is enabled, we set the first bit
|
||||
finalChargingCurrent |= static_cast<quint16>(1) << 15;
|
||||
return;
|
||||
|
||||
} else if (info->action().actionTypeId() == ev11DesiredPhaseCountActionTypeId) {
|
||||
|
||||
uint desiredPhaseCount = info->action().paramValue(ev11DesiredPhaseCountActionDesiredPhaseCountParamTypeId).toUInt();
|
||||
qCDebug(dcPcElectric()) << "Set desried phase count to" << desiredPhaseCount;
|
||||
|
||||
// Update buffer
|
||||
m_chargingCurrentStateBuffer[thing].desiredPhaseCount = desiredPhaseCount;
|
||||
|
||||
quint16 registerValue = PceWallbox::deriveRegisterFromStates(m_chargingCurrentStateBuffer.value(thing));
|
||||
qCDebug(dcPcElectric()) << "Writing charging current register" << registerValue;
|
||||
QueuedModbusReply *reply = connection->setChargingCurrent(registerValue);
|
||||
connect(reply, &QueuedModbusReply::finished, info, [reply, info, thing, desiredPhaseCount](){
|
||||
if (reply->error() != QModbusDevice::NoError) {
|
||||
qCWarning(dcPcElectric()) << "Could not set desired phase count to" << desiredPhaseCount << reply->errorString();
|
||||
info->finish(Thing::ThingErrorHardwareFailure);
|
||||
return;
|
||||
}
|
||||
|
||||
qCDebug(dcPcElectric()) << "Writing charging current register" << finalChargingCurrent << "mA";
|
||||
QueuedModbusReply *reply = connection->setChargingCurrent(finalChargingCurrent);
|
||||
connect(reply, &QueuedModbusReply::finished, info, [reply, finalChargingCurrent](){
|
||||
if (reply->error() != QModbusDevice::NoError) {
|
||||
qCWarning(dcPcElectric()) << "Could not set charging current to" << finalChargingCurrent << "mA" << reply->errorString();
|
||||
return;
|
||||
}
|
||||
qCDebug(dcPcElectric()) << "Successfully set phase count (" << desiredPhaseCount << ")";
|
||||
thing->setStateValue(ev11DesiredPhaseCountStateTypeId, desiredPhaseCount);
|
||||
info->finish(Thing::ThingErrorNoError);
|
||||
});
|
||||
|
||||
qCDebug(dcPcElectric()) << "Successfully set charging current to" << finalChargingCurrent << "mA";
|
||||
});
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
Q_ASSERT_X(false, "IntegrationPluginPcElectric::executeAction", QString("Unhandled action: %1").arg(info->action().actionTypeId().toString()).toLocal8Bit());
|
||||
}
|
||||
|
||||
@ -336,7 +338,7 @@ void IntegrationPluginPcElectric::setupConnection(ThingSetupInfo *info)
|
||||
|
||||
thing->setStateMaxValue(ev11MaxChargingCurrentStateTypeId, connection->maxChargingCurrentDip() / 1000);
|
||||
thing->setStateValue(ev11PluggedInStateTypeId, connection->chargingState() >= PceWallbox::ChargingStateB1 &&
|
||||
connection->chargingState() < PceWallbox::ChargingStateError);
|
||||
connection->chargingState() < PceWallbox::ChargingStateError);
|
||||
|
||||
thing->setStateValue(ev11ChargingStateTypeId, connection->chargingState() == PceWallbox::ChargingStateC2);
|
||||
if (connection->chargingRelayState() != EV11ModbusTcpConnection::ChargingRelayStateNoCharging) {
|
||||
@ -383,6 +385,19 @@ void IntegrationPluginPcElectric::setupConnection(ThingSetupInfo *info)
|
||||
if (m_initialUpdate.value(thing)) {
|
||||
|
||||
m_initialUpdate[thing] = false;
|
||||
|
||||
qCDebug(dcPcElectric()) << "Update initial charger states from charging current register...";
|
||||
|
||||
PceWallbox::ChargingCurrentState chargingCurrentState = PceWallbox::deriveStatesFromRegister(connection->chargingCurrent());
|
||||
qCDebug(dcPcElectric()) << chargingCurrentState;
|
||||
thing->setStateValue(ev11PowerStateTypeId, chargingCurrentState.power);
|
||||
thing->setStateValue(ev11DesiredPhaseCountStateTypeId, chargingCurrentState.desiredPhaseCount);
|
||||
if (chargingCurrentState.power) {
|
||||
thing->setStateValue(ev11MaxChargingCurrentStateTypeId, chargingCurrentState.maxChargingCurrent);
|
||||
}
|
||||
|
||||
m_chargingCurrentStateBuffer[thing] = chargingCurrentState;
|
||||
|
||||
qCDebug(dcPcElectric()) << "Updating initial settings after connecting...";
|
||||
|
||||
thing->setSettingValue(ev11SettingsLedBrightnessParamTypeId, connection->ledBrightness());
|
||||
@ -451,3 +466,4 @@ void IntegrationPluginPcElectric::setupConnection(ThingSetupInfo *info)
|
||||
if (monitor->reachable())
|
||||
connection->connectDevice();
|
||||
}
|
||||
|
||||
|
||||
@ -32,6 +32,7 @@
|
||||
#define INTEGRATIONPLUGINPCELECTRIC_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QDebug>
|
||||
|
||||
#include <integrations/integrationplugin.h>
|
||||
#include <network/networkdevicediscovery.h>
|
||||
@ -59,12 +60,22 @@ public:
|
||||
|
||||
private:
|
||||
PluginTimer *m_refreshTimer = nullptr;
|
||||
|
||||
QHash<Thing *, PceWallbox *> m_connections;
|
||||
QHash<Thing *, NetworkDeviceMonitor *> m_monitors;
|
||||
QHash<Thing *, bool> m_initialUpdate;
|
||||
|
||||
// We need to buffer the desired power / current / phase count states because all 3 states
|
||||
// will be represented by one register (200 - chaegingCurrent). If all 3 actions get executed, they might
|
||||
// overwrite each other, since the action gets started right the way, but the request gets queued.
|
||||
// If the actions would be queued, there would be still the issue with the order of the actions
|
||||
// (set power to false and then set charging current would always enable charging in the end).
|
||||
QHash<Thing *, PceWallbox::ChargingCurrentState> m_chargingCurrentStateBuffer;
|
||||
|
||||
void setupConnection(ThingSetupInfo *info);
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
#endif // INTEGRATIONPLUGINPCELECTRIC_H
|
||||
|
||||
@ -107,6 +107,41 @@ bool PceWallbox::update()
|
||||
|
||||
enqueueRequest(reply);
|
||||
|
||||
// charging current register. Contains
|
||||
// - power state
|
||||
// - chargingcurrent (if power is true)
|
||||
// - phases (if power is true)
|
||||
bool chargingCurrentQueued = false;
|
||||
foreach (QueuedModbusReply *r, m_queue) {
|
||||
if (r->dataUnit().startAddress() == chargingCurrentDataUnit().startAddress()) {
|
||||
chargingCurrentQueued = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!chargingCurrentQueued) {
|
||||
reply = new QueuedModbusReply(QueuedModbusReply::RequestTypeRead, chargingCurrentDataUnit(), this);
|
||||
connect(reply, &QueuedModbusReply::finished, reply, &QueuedModbusReply::deleteLater);
|
||||
connect(reply, &QueuedModbusReply::finished, this, [this, reply](){
|
||||
|
||||
if (m_currentReply == reply)
|
||||
m_currentReply = nullptr;
|
||||
|
||||
if (reply->error() != QModbusDevice::NoError) {
|
||||
QTimer::singleShot(0, this, &PceWallbox::sendNextRequest);
|
||||
return;
|
||||
}
|
||||
|
||||
const QModbusDataUnit unit = reply->reply()->result();
|
||||
const QVector<quint16> values = unit.values();
|
||||
processChargingCurrentRegisterValues(values);
|
||||
|
||||
QTimer::singleShot(0, this, &PceWallbox::sendNextRequest);
|
||||
});
|
||||
|
||||
enqueueRequest(reply);
|
||||
}
|
||||
|
||||
// Digital input
|
||||
bool digitalInputAlreadyQueued = false;
|
||||
foreach (QueuedModbusReply *r, m_queue) {
|
||||
@ -255,6 +290,38 @@ void PceWallbox::gracefullDeleteLater()
|
||||
}
|
||||
}
|
||||
|
||||
quint16 PceWallbox::deriveRegisterFromStates(PceWallbox::ChargingCurrentState state)
|
||||
{
|
||||
quint16 registerValue = 0;
|
||||
if (!state.power)
|
||||
return registerValue; // 0
|
||||
|
||||
registerValue = state.maxChargingCurrent * 1000; // convert to mA
|
||||
if (state.desiredPhaseCount > 1) {
|
||||
registerValue |= static_cast<quint16>(1) << 15;
|
||||
}
|
||||
|
||||
return registerValue;
|
||||
}
|
||||
|
||||
PceWallbox::ChargingCurrentState PceWallbox::deriveStatesFromRegister(quint16 registerValue)
|
||||
{
|
||||
PceWallbox::ChargingCurrentState chargingCurrentState;
|
||||
chargingCurrentState.power = (registerValue != 0);
|
||||
|
||||
// Only set max charging current if power, otherwise we use default 6A
|
||||
if (chargingCurrentState.power) {
|
||||
|
||||
bool threePhaseCharging = (registerValue & (1 << 15));
|
||||
chargingCurrentState.desiredPhaseCount = (threePhaseCharging ? 3 : 1);
|
||||
|
||||
chargingCurrentState.maxChargingCurrent = (registerValue & 0x7FFF) / 1000.0;
|
||||
}
|
||||
|
||||
return chargingCurrentState;
|
||||
}
|
||||
|
||||
|
||||
void PceWallbox::sendHeartbeat()
|
||||
{
|
||||
if (m_aboutToDelete)
|
||||
@ -349,3 +416,10 @@ void PceWallbox::cleanupQueue()
|
||||
qDeleteAll(m_queue);
|
||||
m_queue.clear();
|
||||
}
|
||||
|
||||
QDebug operator<<(QDebug debug, const PceWallbox::ChargingCurrentState &chargingCurrentState)
|
||||
{
|
||||
QDebugStateSaver saver(debug);
|
||||
debug.nospace() << "ChargingCurrentState(" << chargingCurrentState.power << ", " << chargingCurrentState.maxChargingCurrent << " [A], " << chargingCurrentState.desiredPhaseCount << ')';
|
||||
return debug;
|
||||
}
|
||||
|
||||
@ -33,6 +33,7 @@
|
||||
|
||||
#include <QTimer>
|
||||
#include <QQueue>
|
||||
#include <QDebug>
|
||||
#include <QObject>
|
||||
|
||||
#include <queuedmodbusreply.h>
|
||||
@ -43,6 +44,12 @@ class PceWallbox : public EV11ModbusTcpConnection
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
typedef struct ChargingCurrentState {
|
||||
bool power = false;
|
||||
double maxChargingCurrent = 6;
|
||||
uint desiredPhaseCount = 3;
|
||||
} ChargingCurrentState;
|
||||
|
||||
explicit PceWallbox(const QHostAddress &hostAddress, uint port, quint16 slaveId, QObject *parent = nullptr);
|
||||
|
||||
bool update() override;
|
||||
@ -54,7 +61,6 @@ 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 +68,9 @@ public:
|
||||
// IMPORTNAT: do not use the object after this call, this is a temporary workaround
|
||||
void gracefullDeleteLater();
|
||||
|
||||
static quint16 deriveRegisterFromStates(PceWallbox::ChargingCurrentState state);
|
||||
static PceWallbox::ChargingCurrentState deriveStatesFromRegister(quint16 registerValue);
|
||||
|
||||
private slots:
|
||||
void sendHeartbeat();
|
||||
void sendNextRequest();
|
||||
@ -78,4 +87,7 @@ private:
|
||||
void cleanupQueue();
|
||||
};
|
||||
|
||||
QDebug operator<<(QDebug debug, const PceWallbox::ChargingCurrentState &chargingCurrentState);
|
||||
|
||||
|
||||
#endif // PCEWALLBOX_H
|
||||
|
||||
Reference in New Issue
Block a user