647 lines
22 KiB
C++
647 lines
22 KiB
C++
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
|
|
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
|
*
|
|
* Copyright (C) 2013 - 2024, nymea GmbH
|
|
* Copyright (C) 2024 - 2025, chargebyte austria GmbH
|
|
*
|
|
* This file is part of nymea-plugins-modbus.
|
|
*
|
|
* nymea-plugins-modbus 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 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* nymea-plugins-modbus 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 nymea-plugins-modbus. If not, see <https://www.gnu.org/licenses/>.
|
|
*
|
|
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
|
|
|
#include "pcewallbox.h"
|
|
#include "extern-plugininfo.h"
|
|
|
|
#include <modbusdatautils.h>
|
|
|
|
PceWallbox::PceWallbox(const QHostAddress &hostAddress, uint port, quint16 slaveId, QObject *parent)
|
|
: EV11ModbusTcpConnection{hostAddress, port, slaveId, parent}
|
|
{
|
|
// Timer for resetting the heartbeat register (watchdog)
|
|
m_timer.setInterval(30000);
|
|
m_timer.setSingleShot(false);
|
|
connect(&m_timer, &QTimer::timeout, this, &PceWallbox::sendHeartbeat);
|
|
|
|
connect(this, &EV11ModbusTcpConnection::reachableChanged, this, [this](bool reachable) {
|
|
if (!reachable) {
|
|
m_timer.stop();
|
|
|
|
cleanupQueues();
|
|
|
|
if (m_currentReply) {
|
|
m_currentReply = nullptr;
|
|
}
|
|
|
|
} else {
|
|
initialize();
|
|
}
|
|
});
|
|
|
|
connect(this, &EV11ModbusTcpConnection::initializationFinished, this, [this](bool success) {
|
|
if (success) {
|
|
qCDebug(dcPcElectric()) << "Connection initialized successfully" << m_modbusTcpMaster->hostAddress().toString();
|
|
m_timer.start();
|
|
|
|
sendHeartbeat();
|
|
update();
|
|
|
|
} else {
|
|
qCWarning(dcPcElectric()) << "Connection initialization failed for" << m_modbusTcpMaster->hostAddress().toString();
|
|
}
|
|
});
|
|
}
|
|
|
|
bool PceWallbox::update()
|
|
{
|
|
if (m_aboutToDelete)
|
|
return false;
|
|
|
|
if (!reachable())
|
|
return false;
|
|
|
|
// Make sure we only have one update call in the queue
|
|
foreach (QueuedModbusReply *r, m_readQueue) {
|
|
if (r->dataUnit().startAddress() == readBlockInitInfosDataUnit().startAddress()) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
QueuedModbusReply *reply = new QueuedModbusReply(QueuedModbusReply::RequestTypeRead, readBlockStatusDataUnit(), 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> blockValues = unit.values();
|
|
processBlockStatusRegisterValues(blockValues);
|
|
|
|
QTimer::singleShot(0, this, &PceWallbox::sendNextRequest);
|
|
});
|
|
|
|
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_readQueue) {
|
|
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_readQueue) {
|
|
if (r->dataUnit().startAddress() == digitalInputModeDataUnit().startAddress()) {
|
|
digitalInputAlreadyQueued = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
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 (reply->error() != QModbusDevice::NoError) {
|
|
QTimer::singleShot(0, this, &PceWallbox::sendNextRequest);
|
|
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_readQueue) {
|
|
if (r->dataUnit().startAddress() == ledBrightnessDataUnit().startAddress()) {
|
|
ledBrightnessAlreadyQueued = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!ledBrightnessAlreadyQueued) {
|
|
reply = new QueuedModbusReply(QueuedModbusReply::RequestTypeRead, ledBrightnessDataUnit(), 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();
|
|
processLedBrightnessRegisterValues(values);
|
|
|
|
if (firmwareRevision() < "0025")
|
|
emit updateFinished();
|
|
|
|
QTimer::singleShot(0, this, &PceWallbox::sendNextRequest);
|
|
});
|
|
|
|
enqueueRequest(reply);
|
|
}
|
|
|
|
if (firmwareRevision() < "0025")
|
|
return true;
|
|
|
|
// ---------------------------------------------------------------------------------------
|
|
// Registers since 0025 (V 0.25)
|
|
|
|
// Make sure we only have one update 2 call in the queue
|
|
bool update2Queued = false;
|
|
foreach (QueuedModbusReply *r, m_readQueue) {
|
|
if (r->dataUnit().startAddress() == readBlockUpdate2DataUnit().startAddress()) {
|
|
update2Queued = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!update2Queued) {
|
|
reply = new QueuedModbusReply(QueuedModbusReply::RequestTypeRead, readBlockUpdate2DataUnit(), 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) {
|
|
qCWarning(dcPcElectric()) << "Failed to fetch update 2 block" << reply->error() << reply->errorString();
|
|
QTimer::singleShot(0, this, &PceWallbox::sendNextRequest);
|
|
return;
|
|
}
|
|
|
|
const QModbusDataUnit unit = reply->reply()->result();
|
|
const QVector<quint16> blockValues = unit.values();
|
|
processBlockUpdate2RegisterValues(blockValues);
|
|
|
|
QTimer::singleShot(0, this, &PceWallbox::sendNextRequest);
|
|
});
|
|
|
|
enqueueRequest(reply);
|
|
}
|
|
|
|
bool phaseAutoSwitchPauseQueued = false;
|
|
foreach (QueuedModbusReply *r, m_readQueue) {
|
|
if (r->dataUnit().startAddress() == phaseAutoSwitchPauseDataUnit().startAddress()) {
|
|
phaseAutoSwitchPauseQueued = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!phaseAutoSwitchPauseQueued) {
|
|
reply = new QueuedModbusReply(QueuedModbusReply::RequestTypeRead, phaseAutoSwitchPauseDataUnit(), 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();
|
|
processPhaseAutoSwitchPauseRegisterValues(values);
|
|
|
|
QTimer::singleShot(0, this, &PceWallbox::sendNextRequest);
|
|
});
|
|
|
|
enqueueRequest(reply);
|
|
}
|
|
|
|
// Phase auto switch pause (since firmware version 0.25 ...)
|
|
bool phaseAutoSwitchMinChargingTimeQueued = false;
|
|
foreach (QueuedModbusReply *r, m_readQueue) {
|
|
if (r->dataUnit().startAddress() == phaseAutoSwitchMinChargingTimeDataUnit().startAddress()) {
|
|
phaseAutoSwitchMinChargingTimeQueued = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!phaseAutoSwitchMinChargingTimeQueued) {
|
|
reply = new QueuedModbusReply(QueuedModbusReply::RequestTypeRead, phaseAutoSwitchMinChargingTimeDataUnit(), 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();
|
|
processPhaseAutoSwitchMinChargingTimeRegisterValues(values);
|
|
|
|
QTimer::singleShot(0, this, &PceWallbox::sendNextRequest);
|
|
});
|
|
|
|
enqueueRequest(reply);
|
|
}
|
|
|
|
// Phase auto switch pause (since firmware version 0.25 ...)
|
|
bool forceChargingResumeQueued = false;
|
|
foreach (QueuedModbusReply *r, m_readQueue) {
|
|
if (r->dataUnit().startAddress() == forceChargingResumeDataUnit().startAddress()) {
|
|
forceChargingResumeQueued = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!forceChargingResumeQueued) {
|
|
reply = new QueuedModbusReply(QueuedModbusReply::RequestTypeRead, forceChargingResumeDataUnit(), 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();
|
|
processForceChargingResumeRegisterValues(values);
|
|
|
|
emit updateFinished();
|
|
|
|
QTimer::singleShot(0, this, &PceWallbox::sendNextRequest);
|
|
});
|
|
|
|
enqueueRequest(reply);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
QueuedModbusReply *PceWallbox::setChargingCurrentAsync(quint16 chargingCurrent)
|
|
{
|
|
if (m_aboutToDelete)
|
|
return nullptr;
|
|
|
|
QueuedModbusReply *reply = new QueuedModbusReply(QueuedModbusReply::RequestTypeWrite, setChargingCurrentDataUnit(chargingCurrent), this);
|
|
|
|
connect(reply, &QueuedModbusReply::finished, reply, &QueuedModbusReply::deleteLater);
|
|
connect(reply, &QueuedModbusReply::finished, this, [this, reply]() {
|
|
if (m_currentReply == reply)
|
|
m_currentReply = nullptr;
|
|
|
|
QTimer::singleShot(0, this, &PceWallbox::sendNextRequest);
|
|
return;
|
|
});
|
|
|
|
enqueueRequest(reply);
|
|
return reply;
|
|
}
|
|
|
|
QueuedModbusReply *PceWallbox::setLedBrightnessAsync(quint16 percentage)
|
|
{
|
|
if (m_aboutToDelete)
|
|
return nullptr;
|
|
|
|
QueuedModbusReply *reply = new QueuedModbusReply(QueuedModbusReply::RequestTypeWrite, setLedBrightnessDataUnit(percentage), this);
|
|
|
|
connect(reply, &QueuedModbusReply::finished, reply, &QueuedModbusReply::deleteLater);
|
|
connect(reply, &QueuedModbusReply::finished, this, [this, reply]() {
|
|
if (m_currentReply == reply)
|
|
m_currentReply = nullptr;
|
|
|
|
QTimer::singleShot(0, this, &PceWallbox::sendNextRequest);
|
|
return;
|
|
});
|
|
|
|
enqueueRequest(reply);
|
|
return reply;
|
|
}
|
|
|
|
QueuedModbusReply *PceWallbox::setPhaseAutoSwitchPauseAsync(quint16 seconds)
|
|
{
|
|
if (m_aboutToDelete)
|
|
return nullptr;
|
|
|
|
QueuedModbusReply *reply = new QueuedModbusReply(QueuedModbusReply::RequestTypeWrite, setPhaseAutoSwitchPauseDataUnit(seconds), this);
|
|
|
|
connect(reply, &QueuedModbusReply::finished, reply, &QueuedModbusReply::deleteLater);
|
|
connect(reply, &QueuedModbusReply::finished, this, [this, reply]() {
|
|
if (m_currentReply == reply)
|
|
m_currentReply = nullptr;
|
|
|
|
QTimer::singleShot(0, this, &PceWallbox::sendNextRequest);
|
|
return;
|
|
});
|
|
|
|
enqueueRequest(reply);
|
|
return reply;
|
|
}
|
|
|
|
QueuedModbusReply *PceWallbox::setPhaseAutoSwitchMinChargingTimeAsync(quint16 seconds)
|
|
{
|
|
if (m_aboutToDelete)
|
|
return nullptr;
|
|
|
|
QueuedModbusReply *reply = new QueuedModbusReply(QueuedModbusReply::RequestTypeWrite, setPhaseAutoSwitchMinChargingTimeDataUnit(seconds), this);
|
|
|
|
connect(reply, &QueuedModbusReply::finished, reply, &QueuedModbusReply::deleteLater);
|
|
connect(reply, &QueuedModbusReply::finished, this, [this, reply]() {
|
|
if (m_currentReply == reply)
|
|
m_currentReply = nullptr;
|
|
|
|
QTimer::singleShot(0, this, &PceWallbox::sendNextRequest);
|
|
return;
|
|
});
|
|
|
|
enqueueRequest(reply);
|
|
return reply;
|
|
}
|
|
|
|
QueuedModbusReply *PceWallbox::setForceChargingResumeAsync(quint16 value)
|
|
{
|
|
if (m_aboutToDelete)
|
|
return nullptr;
|
|
|
|
QueuedModbusReply *reply = new QueuedModbusReply(QueuedModbusReply::RequestTypeWrite, setForceChargingResumeDataUnit(value), this);
|
|
|
|
connect(reply, &QueuedModbusReply::finished, reply, &QueuedModbusReply::deleteLater);
|
|
connect(reply, &QueuedModbusReply::finished, this, [this, reply]() {
|
|
if (m_currentReply == reply)
|
|
m_currentReply = nullptr;
|
|
|
|
QTimer::singleShot(0, this, &PceWallbox::sendNextRequest);
|
|
return;
|
|
});
|
|
|
|
enqueueRequest(reply);
|
|
return reply;
|
|
}
|
|
|
|
QueuedModbusReply *PceWallbox::setDigitalInputModeAsync(DigitalInputMode digitalInputMode)
|
|
{
|
|
if (m_aboutToDelete)
|
|
return nullptr;
|
|
|
|
QueuedModbusReply *reply = new QueuedModbusReply(QueuedModbusReply::RequestTypeWrite, setDigitalInputModeDataUnit(digitalInputMode), this);
|
|
|
|
connect(reply, &QueuedModbusReply::finished, reply, &QueuedModbusReply::deleteLater);
|
|
connect(reply, &QueuedModbusReply::finished, this, [this, reply]() {
|
|
if (m_currentReply == reply)
|
|
m_currentReply = nullptr;
|
|
|
|
QTimer::singleShot(0, this, &PceWallbox::sendNextRequest);
|
|
return;
|
|
});
|
|
|
|
enqueueRequest(reply);
|
|
return reply;
|
|
}
|
|
|
|
void PceWallbox::gracefullDeleteLater()
|
|
{
|
|
// Clean up the queue
|
|
m_aboutToDelete = true;
|
|
cleanupQueues();
|
|
|
|
m_timer.stop();
|
|
|
|
if (!m_currentReply) {
|
|
qCDebug(dcPcElectric()) << "Deleting object without pending request...";
|
|
// No pending request, we can close the connection and delete the object
|
|
disconnect(this, nullptr, nullptr, nullptr);
|
|
disconnectDevice();
|
|
deleteLater();
|
|
} else {
|
|
qCDebug(dcPcElectric()) << "Pending request, deleting object once the request is finished...";
|
|
}
|
|
}
|
|
|
|
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)
|
|
return;
|
|
|
|
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;
|
|
|
|
if (reply->error() != QModbusDevice::NoError) {
|
|
qCWarning(dcPcElectric()) << "Failed to send heartbeat to" << m_modbusTcpMaster->hostAddress().toString() << reply->errorString();
|
|
} else {
|
|
qCDebug(dcPcElectric()) << "Successfully sent heartbeat to" << m_modbusTcpMaster->hostAddress().toString();
|
|
}
|
|
|
|
QTimer::singleShot(0, this, &PceWallbox::sendNextRequest);
|
|
return;
|
|
});
|
|
|
|
enqueueRequest(reply);
|
|
}
|
|
|
|
void PceWallbox::sendNextRequest()
|
|
{
|
|
if (m_writeQueue.isEmpty() && m_readQueue.isEmpty())
|
|
return;
|
|
|
|
if (m_currentReply)
|
|
return;
|
|
|
|
if (m_aboutToDelete) {
|
|
disconnect(this, nullptr, nullptr, nullptr);
|
|
disconnectDevice();
|
|
deleteLater();
|
|
return;
|
|
}
|
|
|
|
// Note: due to the fact that we have one register which controls 3 states,
|
|
// the order of the execution is critical at this point. We have to make sure
|
|
// the register gets written in the same order as they where requested by the action
|
|
// execution (and the dedicated ChargingCurrentState buffer)
|
|
|
|
if (!m_writeQueue.isEmpty()) {
|
|
// Prioritize write requests
|
|
m_currentReply = m_writeQueue.dequeue();
|
|
qCDebug(dcPcElectric()) << "Dequeued write request. Queue count: W" << m_writeQueue.length() << "| R:" << m_readQueue.length();
|
|
} else {
|
|
m_currentReply = m_readQueue.dequeue();
|
|
qCDebug(dcPcElectric()) << "Dequeued read request. Queue count: W" << m_writeQueue.length() << "| R:" << m_readQueue.length();
|
|
}
|
|
|
|
switch (m_currentReply->requestType()) {
|
|
case QueuedModbusReply::RequestTypeRead:
|
|
qCDebug(dcPcElectric())
|
|
<< "--> Reading"
|
|
<< ModbusDataUtils::registerTypeToString(m_currentReply->dataUnit().registerType())
|
|
<< "register:"
|
|
<< m_currentReply->dataUnit().startAddress()
|
|
<< "length"
|
|
<< m_currentReply->dataUnit().valueCount();
|
|
m_currentReply->setReply(m_modbusTcpMaster->sendReadRequest(m_currentReply->dataUnit(), m_slaveId));
|
|
break;
|
|
case QueuedModbusReply::RequestTypeWrite:
|
|
qCDebug(dcPcElectric())
|
|
<< "--> Writing"
|
|
<< ModbusDataUtils::registerTypeToString(m_currentReply->dataUnit().registerType())
|
|
<< "register:"
|
|
<< m_currentReply->dataUnit().startAddress()
|
|
<< "length:"
|
|
<< m_currentReply->dataUnit().valueCount()
|
|
<< "values:"
|
|
<< m_currentReply->dataUnit().values();
|
|
m_currentReply->setReply(m_modbusTcpMaster->sendWriteRequest(m_currentReply->dataUnit(), m_slaveId));
|
|
break;
|
|
}
|
|
|
|
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();
|
|
m_currentReply->deleteLater();
|
|
m_currentReply = nullptr;
|
|
QTimer::singleShot(0, this, &PceWallbox::sendNextRequest);
|
|
return;
|
|
}
|
|
|
|
if (m_currentReply->reply()->isFinished()) {
|
|
qCWarning(dcPcElectric()) << "Reply immediatly finished";
|
|
m_currentReply->deleteLater();
|
|
m_currentReply = nullptr;
|
|
QTimer::singleShot(0, this, &PceWallbox::sendNextRequest);
|
|
return;
|
|
}
|
|
}
|
|
|
|
void PceWallbox::enqueueRequest(QueuedModbusReply *reply)
|
|
{
|
|
switch (reply->requestType()) {
|
|
case QueuedModbusReply::RequestTypeRead:
|
|
m_readQueue.enqueue(reply);
|
|
break;
|
|
case QueuedModbusReply::RequestTypeWrite:
|
|
m_writeQueue.enqueue(reply);
|
|
break;
|
|
}
|
|
|
|
QTimer::singleShot(0, this, &PceWallbox::sendNextRequest);
|
|
}
|
|
|
|
void PceWallbox::cleanupQueues()
|
|
{
|
|
qDeleteAll(m_readQueue);
|
|
m_readQueue.clear();
|
|
|
|
qDeleteAll(m_writeQueue);
|
|
m_writeQueue.clear();
|
|
}
|
|
|
|
QDebug operator<<(QDebug debug, const PceWallbox::ChargingCurrentState &chargingCurrentState)
|
|
{
|
|
QDebugStateSaver saver(debug);
|
|
debug.nospace()
|
|
<< "ChargingCurrentState("
|
|
<< chargingCurrentState.power
|
|
<< ", "
|
|
<< chargingCurrentState.maxChargingCurrent
|
|
<< " [A], "
|
|
<< chargingCurrentState.desiredPhaseCount
|
|
<< ')';
|
|
return debug;
|
|
}
|