nymea-plugins-modbus/libnymea-sunspec/models/sunspecaggregatormodel.cpp

356 lines
13 KiB
C++

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* Copyright 2013 - 2025, nymea GmbH
* Contact: contact@nymea.io
*
* This fileDescriptor is part of nymea.
* This project including source code and documentation is protected by
* copyright law, and remains the property of nymea GmbH. All rights, including
* reproduction, publication, editing and translation, are reserved. The use of
* this project is subject to the terms of a license agreement to be concluded
* with nymea GmbH in accordance with the terms of use of nymea GmbH, available
* under https://nymea.io/license
*
* GNU Lesser General Public License Usage
* Alternatively, this project may be redistributed and/or modified under the
* terms of the GNU Lesser General Public License as published by the Free
* Software Foundation; version 3. This project 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 this project. If not, see <https://www.gnu.org/licenses/>.
*
* For any further details and any questions please contact us under
* contact@nymea.io or see our FAQ/Licensing Information on
* https://nymea.io/license/faq
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#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();
}