350 lines
13 KiB
C++
350 lines
13 KiB
C++
// SPDX-License-Identifier: LGPL-3.0-or-later
|
|
|
|
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
|
*
|
|
* Copyright (C) 2013 - 2024, nymea GmbH
|
|
* Copyright (C) 2024 - 2025, chargebyte austria GmbH
|
|
*
|
|
* This file is part of libnymea-sunspec.
|
|
*
|
|
* libnymea-sunspec is free software: you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public License
|
|
* as published by the Free Software Foundation, either version 3
|
|
* of the License, or (at your option) any later version.
|
|
*
|
|
* libnymea-sunspec 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 Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public License
|
|
* along with libnymea-sunspec. If not, see <https://www.gnu.org/licenses/>.
|
|
*
|
|
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
|
|
|
#include "sunspecaggregatormodel.h"
|
|
#include "sunspecconnection.h"
|
|
|
|
SunSpecAggregatorModel::SunSpecAggregatorModel(SunSpecConnection *connection, quint16 modbusStartRegister, quint16 modelLength, SunSpecDataPoint::ByteOrder byteOrder, QObject *parent) :
|
|
SunSpecModel(connection, modbusStartRegister, 2, modelLength, byteOrder, parent)
|
|
{
|
|
m_modelBlockType = SunSpecModel::ModelBlockTypeFixed;
|
|
|
|
initDataPoints();
|
|
}
|
|
|
|
SunSpecAggregatorModel::~SunSpecAggregatorModel()
|
|
{
|
|
|
|
}
|
|
|
|
QString SunSpecAggregatorModel::name() const
|
|
{
|
|
return "aggregator";
|
|
}
|
|
|
|
QString SunSpecAggregatorModel::description() const
|
|
{
|
|
return "Aggregates a collection of models for a given model id";
|
|
}
|
|
|
|
QString SunSpecAggregatorModel::label() const
|
|
{
|
|
return "Basic Aggregator";
|
|
}
|
|
|
|
quint16 SunSpecAggregatorModel::aid() const
|
|
{
|
|
return m_aid;
|
|
}
|
|
quint16 SunSpecAggregatorModel::n() const
|
|
{
|
|
return m_n;
|
|
}
|
|
quint16 SunSpecAggregatorModel::un() const
|
|
{
|
|
return m_un;
|
|
}
|
|
SunSpecAggregatorModel::St SunSpecAggregatorModel::status() const
|
|
{
|
|
return m_status;
|
|
}
|
|
quint16 SunSpecAggregatorModel::vendorStatus() const
|
|
{
|
|
return m_vendorStatus;
|
|
}
|
|
SunSpecAggregatorModel::EvtFlags SunSpecAggregatorModel::eventCode() const
|
|
{
|
|
return m_eventCode;
|
|
}
|
|
quint32 SunSpecAggregatorModel::vendorEventCode() const
|
|
{
|
|
return m_vendorEventCode;
|
|
}
|
|
SunSpecAggregatorModel::Ctl SunSpecAggregatorModel::control() const
|
|
{
|
|
return m_control;
|
|
}
|
|
quint32 SunSpecAggregatorModel::vendorControl() const
|
|
{
|
|
return m_vendorControl;
|
|
}
|
|
quint32 SunSpecAggregatorModel::controlValue() const
|
|
{
|
|
return m_controlValue;
|
|
}
|
|
void SunSpecAggregatorModel::initDataPoints()
|
|
{
|
|
SunSpecDataPoint modelIdDataPoint;
|
|
modelIdDataPoint.setName("ID");
|
|
modelIdDataPoint.setLabel("Model ID");
|
|
modelIdDataPoint.setDescription("Model identifier");
|
|
modelIdDataPoint.setMandatory(true);
|
|
modelIdDataPoint.setSize(1);
|
|
modelIdDataPoint.setAddressOffset(0);
|
|
modelIdDataPoint.setSunSpecDataType("uint16");
|
|
modelIdDataPoint.setByteOrder(m_byteOrder);
|
|
m_dataPoints.insert(modelIdDataPoint.name(), modelIdDataPoint);
|
|
|
|
SunSpecDataPoint modelLengthDataPoint;
|
|
modelLengthDataPoint.setName("L");
|
|
modelLengthDataPoint.setLabel("Model Length");
|
|
modelLengthDataPoint.setDescription("Model length");
|
|
modelLengthDataPoint.setMandatory(true);
|
|
modelLengthDataPoint.setSize(1);
|
|
modelLengthDataPoint.setAddressOffset(1);
|
|
modelLengthDataPoint.setSunSpecDataType("uint16");
|
|
modelLengthDataPoint.setByteOrder(m_byteOrder);
|
|
m_dataPoints.insert(modelLengthDataPoint.name(), modelLengthDataPoint);
|
|
|
|
SunSpecDataPoint aidDataPoint;
|
|
aidDataPoint.setName("AID");
|
|
aidDataPoint.setLabel("AID");
|
|
aidDataPoint.setDescription("Aggregated model id");
|
|
aidDataPoint.setMandatory(true);
|
|
aidDataPoint.setSize(1);
|
|
aidDataPoint.setAddressOffset(2);
|
|
aidDataPoint.setBlockOffset(0);
|
|
aidDataPoint.setSunSpecDataType("uint16");
|
|
aidDataPoint.setByteOrder(m_byteOrder);
|
|
m_dataPoints.insert(aidDataPoint.name(), aidDataPoint);
|
|
|
|
SunSpecDataPoint nDataPoint;
|
|
nDataPoint.setName("N");
|
|
nDataPoint.setLabel("N");
|
|
nDataPoint.setDescription("Number of aggregated models");
|
|
nDataPoint.setMandatory(true);
|
|
nDataPoint.setSize(1);
|
|
nDataPoint.setAddressOffset(3);
|
|
nDataPoint.setBlockOffset(1);
|
|
nDataPoint.setSunSpecDataType("uint16");
|
|
nDataPoint.setByteOrder(m_byteOrder);
|
|
m_dataPoints.insert(nDataPoint.name(), nDataPoint);
|
|
|
|
SunSpecDataPoint unDataPoint;
|
|
unDataPoint.setName("UN");
|
|
unDataPoint.setLabel("UN");
|
|
unDataPoint.setDescription("Update Number. Incrementing number each time the mapping is changed. If the number is not changed from the last reading the direct access to a specific offset will result in reading the same logical model as before. Otherwise the entire model must be read to refresh the changes");
|
|
unDataPoint.setMandatory(true);
|
|
unDataPoint.setSize(1);
|
|
unDataPoint.setAddressOffset(4);
|
|
unDataPoint.setBlockOffset(2);
|
|
unDataPoint.setSunSpecDataType("uint16");
|
|
unDataPoint.setByteOrder(m_byteOrder);
|
|
m_dataPoints.insert(unDataPoint.name(), unDataPoint);
|
|
|
|
SunSpecDataPoint statusDataPoint;
|
|
statusDataPoint.setName("St");
|
|
statusDataPoint.setLabel("Status");
|
|
statusDataPoint.setDescription("Enumerated status code");
|
|
statusDataPoint.setMandatory(true);
|
|
statusDataPoint.setSize(1);
|
|
statusDataPoint.setAddressOffset(5);
|
|
statusDataPoint.setBlockOffset(3);
|
|
statusDataPoint.setSunSpecDataType("enum16");
|
|
statusDataPoint.setByteOrder(m_byteOrder);
|
|
m_dataPoints.insert(statusDataPoint.name(), statusDataPoint);
|
|
|
|
SunSpecDataPoint vendorStatusDataPoint;
|
|
vendorStatusDataPoint.setName("StVnd");
|
|
vendorStatusDataPoint.setLabel("Vendor Status");
|
|
vendorStatusDataPoint.setDescription("Vendor specific status code");
|
|
vendorStatusDataPoint.setSize(1);
|
|
vendorStatusDataPoint.setAddressOffset(6);
|
|
vendorStatusDataPoint.setBlockOffset(4);
|
|
vendorStatusDataPoint.setSunSpecDataType("enum16");
|
|
vendorStatusDataPoint.setByteOrder(m_byteOrder);
|
|
m_dataPoints.insert(vendorStatusDataPoint.name(), vendorStatusDataPoint);
|
|
|
|
SunSpecDataPoint eventCodeDataPoint;
|
|
eventCodeDataPoint.setName("Evt");
|
|
eventCodeDataPoint.setLabel("Event Code");
|
|
eventCodeDataPoint.setDescription("Bitmask event code");
|
|
eventCodeDataPoint.setMandatory(true);
|
|
eventCodeDataPoint.setSize(2);
|
|
eventCodeDataPoint.setAddressOffset(7);
|
|
eventCodeDataPoint.setBlockOffset(5);
|
|
eventCodeDataPoint.setSunSpecDataType("bitfield32");
|
|
eventCodeDataPoint.setByteOrder(m_byteOrder);
|
|
m_dataPoints.insert(eventCodeDataPoint.name(), eventCodeDataPoint);
|
|
|
|
SunSpecDataPoint vendorEventCodeDataPoint;
|
|
vendorEventCodeDataPoint.setName("EvtVnd");
|
|
vendorEventCodeDataPoint.setLabel("Vendor Event Code");
|
|
vendorEventCodeDataPoint.setDescription("Vendor specific event code");
|
|
vendorEventCodeDataPoint.setSize(2);
|
|
vendorEventCodeDataPoint.setAddressOffset(9);
|
|
vendorEventCodeDataPoint.setBlockOffset(7);
|
|
vendorEventCodeDataPoint.setSunSpecDataType("bitfield32");
|
|
vendorEventCodeDataPoint.setByteOrder(m_byteOrder);
|
|
m_dataPoints.insert(vendorEventCodeDataPoint.name(), vendorEventCodeDataPoint);
|
|
|
|
SunSpecDataPoint controlDataPoint;
|
|
controlDataPoint.setName("Ctl");
|
|
controlDataPoint.setLabel("Control");
|
|
controlDataPoint.setDescription("Control register for all aggregated devices");
|
|
controlDataPoint.setSize(1);
|
|
controlDataPoint.setAddressOffset(11);
|
|
controlDataPoint.setBlockOffset(9);
|
|
controlDataPoint.setSunSpecDataType("enum16");
|
|
controlDataPoint.setByteOrder(m_byteOrder);
|
|
m_dataPoints.insert(controlDataPoint.name(), controlDataPoint);
|
|
|
|
SunSpecDataPoint vendorControlDataPoint;
|
|
vendorControlDataPoint.setName("CtlVnd");
|
|
vendorControlDataPoint.setLabel("Vendor Control");
|
|
vendorControlDataPoint.setDescription("Vendor control register for all aggregated devices");
|
|
vendorControlDataPoint.setSize(2);
|
|
vendorControlDataPoint.setAddressOffset(12);
|
|
vendorControlDataPoint.setBlockOffset(10);
|
|
vendorControlDataPoint.setSunSpecDataType("enum32");
|
|
vendorControlDataPoint.setByteOrder(m_byteOrder);
|
|
m_dataPoints.insert(vendorControlDataPoint.name(), vendorControlDataPoint);
|
|
|
|
SunSpecDataPoint controlValueDataPoint;
|
|
controlValueDataPoint.setName("CtlVl");
|
|
controlValueDataPoint.setLabel("Control Value");
|
|
controlValueDataPoint.setDescription("Numerical value used as a parameter to the control");
|
|
controlValueDataPoint.setSize(2);
|
|
controlValueDataPoint.setAddressOffset(14);
|
|
controlValueDataPoint.setBlockOffset(12);
|
|
controlValueDataPoint.setSunSpecDataType("enum32");
|
|
controlValueDataPoint.setByteOrder(m_byteOrder);
|
|
m_dataPoints.insert(controlValueDataPoint.name(), controlValueDataPoint);
|
|
|
|
}
|
|
|
|
void SunSpecAggregatorModel::processBlockData()
|
|
{
|
|
// Update properties according to the data point type
|
|
if (m_dataPoints.value("AID").isValid())
|
|
m_aid = m_dataPoints.value("AID").toUInt16();
|
|
|
|
if (m_dataPoints.value("N").isValid())
|
|
m_n = m_dataPoints.value("N").toUInt16();
|
|
|
|
if (m_dataPoints.value("UN").isValid())
|
|
m_un = m_dataPoints.value("UN").toUInt16();
|
|
|
|
if (m_dataPoints.value("St").isValid())
|
|
m_status = static_cast<St>(m_dataPoints.value("St").toUInt16());
|
|
|
|
if (m_dataPoints.value("StVnd").isValid())
|
|
m_vendorStatus = m_dataPoints.value("StVnd").toUInt16();
|
|
|
|
if (m_dataPoints.value("Evt").isValid())
|
|
m_eventCode = static_cast<EvtFlags>(m_dataPoints.value("Evt").toUInt32());
|
|
|
|
if (m_dataPoints.value("EvtVnd").isValid())
|
|
m_vendorEventCode = m_dataPoints.value("EvtVnd").toUInt32();
|
|
|
|
if (m_dataPoints.value("Ctl").isValid())
|
|
m_control = static_cast<Ctl>(m_dataPoints.value("Ctl").toUInt16());
|
|
|
|
if (m_dataPoints.value("CtlVnd").isValid())
|
|
m_vendorControl = m_dataPoints.value("CtlVnd").toUInt32();
|
|
|
|
if (m_dataPoints.value("CtlVl").isValid())
|
|
m_controlValue = m_dataPoints.value("CtlVl").toUInt32();
|
|
|
|
|
|
qCDebug(dcSunSpecModelData()) << this;
|
|
}
|
|
|
|
QDebug operator<<(QDebug debug, SunSpecAggregatorModel *model)
|
|
{
|
|
debug.nospace().noquote() << "SunSpecAggregatorModel(Model: " << model->modelId() << ", Register: " << model->modbusStartRegister() << ", Length: " << model->modelLength() << ")\n";
|
|
debug.nospace().noquote() << " - " << model->dataPoints().value("AID") << "-->";
|
|
if (model->dataPoints().value("AID").isValid()) {
|
|
debug.nospace().noquote() << model->aid() << "\n";
|
|
} else {
|
|
debug.nospace().noquote() << "NaN\n";
|
|
}
|
|
|
|
debug.nospace().noquote() << " - " << model->dataPoints().value("N") << "-->";
|
|
if (model->dataPoints().value("N").isValid()) {
|
|
debug.nospace().noquote() << model->n() << "\n";
|
|
} else {
|
|
debug.nospace().noquote() << "NaN\n";
|
|
}
|
|
|
|
debug.nospace().noquote() << " - " << model->dataPoints().value("UN") << "-->";
|
|
if (model->dataPoints().value("UN").isValid()) {
|
|
debug.nospace().noquote() << model->un() << "\n";
|
|
} else {
|
|
debug.nospace().noquote() << "NaN\n";
|
|
}
|
|
|
|
debug.nospace().noquote() << " - " << model->dataPoints().value("St") << "-->";
|
|
if (model->dataPoints().value("St").isValid()) {
|
|
debug.nospace().noquote() << model->status() << "\n";
|
|
} else {
|
|
debug.nospace().noquote() << "NaN\n";
|
|
}
|
|
|
|
debug.nospace().noquote() << " - " << model->dataPoints().value("StVnd") << "-->";
|
|
if (model->dataPoints().value("StVnd").isValid()) {
|
|
debug.nospace().noquote() << model->vendorStatus() << "\n";
|
|
} else {
|
|
debug.nospace().noquote() << "NaN\n";
|
|
}
|
|
|
|
debug.nospace().noquote() << " - " << model->dataPoints().value("Evt") << "-->";
|
|
if (model->dataPoints().value("Evt").isValid()) {
|
|
debug.nospace().noquote() << model->eventCode() << "\n";
|
|
} else {
|
|
debug.nospace().noquote() << "NaN\n";
|
|
}
|
|
|
|
debug.nospace().noquote() << " - " << model->dataPoints().value("EvtVnd") << "-->";
|
|
if (model->dataPoints().value("EvtVnd").isValid()) {
|
|
debug.nospace().noquote() << model->vendorEventCode() << "\n";
|
|
} else {
|
|
debug.nospace().noquote() << "NaN\n";
|
|
}
|
|
|
|
debug.nospace().noquote() << " - " << model->dataPoints().value("Ctl") << "-->";
|
|
if (model->dataPoints().value("Ctl").isValid()) {
|
|
debug.nospace().noquote() << model->control() << "\n";
|
|
} else {
|
|
debug.nospace().noquote() << "NaN\n";
|
|
}
|
|
|
|
debug.nospace().noquote() << " - " << model->dataPoints().value("CtlVnd") << "-->";
|
|
if (model->dataPoints().value("CtlVnd").isValid()) {
|
|
debug.nospace().noquote() << model->vendorControl() << "\n";
|
|
} else {
|
|
debug.nospace().noquote() << "NaN\n";
|
|
}
|
|
|
|
debug.nospace().noquote() << " - " << model->dataPoints().value("CtlVl") << "-->";
|
|
if (model->dataPoints().value("CtlVl").isValid()) {
|
|
debug.nospace().noquote() << model->controlValue() << "\n";
|
|
} else {
|
|
debug.nospace().noquote() << "NaN\n";
|
|
}
|
|
|
|
|
|
return debug.space().quote();
|
|
}
|