442 lines
17 KiB
C++
442 lines
17 KiB
C++
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
|
*
|
|
* Copyright 2013 - 2021, 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 "sunspecfreqwattparammodel.h"
|
|
#include "sunspecconnection.h"
|
|
|
|
SunSpecFreqWattParamModel::SunSpecFreqWattParamModel(SunSpecConnection *connection, quint16 modbusStartRegister, quint16 modelLength, SunSpecDataPoint::ByteOrder byteOrder, QObject *parent) :
|
|
SunSpecModel(connection, modbusStartRegister, 127, modelLength, byteOrder, parent)
|
|
{
|
|
m_modelBlockType = SunSpecModel::ModelBlockTypeFixed;
|
|
|
|
initDataPoints();
|
|
}
|
|
|
|
SunSpecFreqWattParamModel::~SunSpecFreqWattParamModel()
|
|
{
|
|
|
|
}
|
|
|
|
QString SunSpecFreqWattParamModel::name() const
|
|
{
|
|
return "freq_watt_param";
|
|
}
|
|
|
|
QString SunSpecFreqWattParamModel::description() const
|
|
{
|
|
return "Parameterized Frequency-Watt ";
|
|
}
|
|
|
|
QString SunSpecFreqWattParamModel::label() const
|
|
{
|
|
return "Freq-Watt Param";
|
|
}
|
|
|
|
float SunSpecFreqWattParamModel::wGra() const
|
|
{
|
|
return m_wGra;
|
|
}
|
|
|
|
QModbusReply *SunSpecFreqWattParamModel::setWGra(float wGra)
|
|
{
|
|
if (!m_initialized)
|
|
return nullptr;
|
|
|
|
SunSpecDataPoint dp = m_dataPoints.value("WGra");
|
|
QVector<quint16> registers = SunSpecDataPoint::convertFromFloatWithSSF(wGra, m_wGraSf, dp.dataType());
|
|
|
|
QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::HoldingRegisters, m_modbusStartRegister + dp.addressOffset(), registers.length());
|
|
request.setValues(registers);
|
|
|
|
return m_connection->modbusTcpClient()->sendWriteRequest(request, m_connection->slaveId());
|
|
}
|
|
float SunSpecFreqWattParamModel::hzStr() const
|
|
{
|
|
return m_hzStr;
|
|
}
|
|
|
|
QModbusReply *SunSpecFreqWattParamModel::setHzStr(float hzStr)
|
|
{
|
|
if (!m_initialized)
|
|
return nullptr;
|
|
|
|
SunSpecDataPoint dp = m_dataPoints.value("HzStr");
|
|
QVector<quint16> registers = SunSpecDataPoint::convertFromFloatWithSSF(hzStr, m_hzStrStopSf, dp.dataType());
|
|
|
|
QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::HoldingRegisters, m_modbusStartRegister + dp.addressOffset(), registers.length());
|
|
request.setValues(registers);
|
|
|
|
return m_connection->modbusTcpClient()->sendWriteRequest(request, m_connection->slaveId());
|
|
}
|
|
float SunSpecFreqWattParamModel::hzStop() const
|
|
{
|
|
return m_hzStop;
|
|
}
|
|
|
|
QModbusReply *SunSpecFreqWattParamModel::setHzStop(float hzStop)
|
|
{
|
|
if (!m_initialized)
|
|
return nullptr;
|
|
|
|
SunSpecDataPoint dp = m_dataPoints.value("HzStop");
|
|
QVector<quint16> registers = SunSpecDataPoint::convertFromFloatWithSSF(hzStop, m_hzStrStopSf, dp.dataType());
|
|
|
|
QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::HoldingRegisters, m_modbusStartRegister + dp.addressOffset(), registers.length());
|
|
request.setValues(registers);
|
|
|
|
return m_connection->modbusTcpClient()->sendWriteRequest(request, m_connection->slaveId());
|
|
}
|
|
SunSpecFreqWattParamModel::HysenaFlags SunSpecFreqWattParamModel::hysEna() const
|
|
{
|
|
return m_hysEna;
|
|
}
|
|
|
|
QModbusReply *SunSpecFreqWattParamModel::setHysEna(HysenaFlags hysEna)
|
|
{
|
|
if (!m_initialized)
|
|
return nullptr;
|
|
|
|
SunSpecDataPoint dp = m_dataPoints.value("HysEna");
|
|
QVector<quint16> registers = SunSpecDataPoint::convertFromUInt16(static_cast<quint16>(hysEna));
|
|
|
|
QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::HoldingRegisters, m_modbusStartRegister + dp.addressOffset(), registers.length());
|
|
request.setValues(registers);
|
|
|
|
return m_connection->modbusTcpClient()->sendWriteRequest(request, m_connection->slaveId());
|
|
}
|
|
SunSpecFreqWattParamModel::ModenaFlags SunSpecFreqWattParamModel::modEna() const
|
|
{
|
|
return m_modEna;
|
|
}
|
|
|
|
QModbusReply *SunSpecFreqWattParamModel::setModEna(ModenaFlags modEna)
|
|
{
|
|
if (!m_initialized)
|
|
return nullptr;
|
|
|
|
SunSpecDataPoint dp = m_dataPoints.value("ModEna");
|
|
QVector<quint16> registers = SunSpecDataPoint::convertFromUInt16(static_cast<quint16>(modEna));
|
|
|
|
QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::HoldingRegisters, m_modbusStartRegister + dp.addressOffset(), registers.length());
|
|
request.setValues(registers);
|
|
|
|
return m_connection->modbusTcpClient()->sendWriteRequest(request, m_connection->slaveId());
|
|
}
|
|
float SunSpecFreqWattParamModel::hzStopWGra() const
|
|
{
|
|
return m_hzStopWGra;
|
|
}
|
|
|
|
QModbusReply *SunSpecFreqWattParamModel::setHzStopWGra(float hzStopWGra)
|
|
{
|
|
if (!m_initialized)
|
|
return nullptr;
|
|
|
|
SunSpecDataPoint dp = m_dataPoints.value("HzStopWGra");
|
|
QVector<quint16> registers = SunSpecDataPoint::convertFromFloatWithSSF(hzStopWGra, m_rmpIncDecSf, dp.dataType());
|
|
|
|
QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::HoldingRegisters, m_modbusStartRegister + dp.addressOffset(), registers.length());
|
|
request.setValues(registers);
|
|
|
|
return m_connection->modbusTcpClient()->sendWriteRequest(request, m_connection->slaveId());
|
|
}
|
|
qint16 SunSpecFreqWattParamModel::wGraSf() const
|
|
{
|
|
return m_wGraSf;
|
|
}
|
|
qint16 SunSpecFreqWattParamModel::hzStrStopSf() const
|
|
{
|
|
return m_hzStrStopSf;
|
|
}
|
|
qint16 SunSpecFreqWattParamModel::rmpIncDecSf() const
|
|
{
|
|
return m_rmpIncDecSf;
|
|
}
|
|
quint16 SunSpecFreqWattParamModel::pad() const
|
|
{
|
|
return m_pad;
|
|
}
|
|
void SunSpecFreqWattParamModel::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 wGraDataPoint;
|
|
wGraDataPoint.setName("WGra");
|
|
wGraDataPoint.setLabel("WGra");
|
|
wGraDataPoint.setDescription("The slope of the reduction in the maximum allowed watts output as a function of frequency.");
|
|
wGraDataPoint.setUnits("% PM/Hz");
|
|
wGraDataPoint.setMandatory(true);
|
|
wGraDataPoint.setSize(1);
|
|
wGraDataPoint.setAddressOffset(2);
|
|
wGraDataPoint.setBlockOffset(0);
|
|
wGraDataPoint.setScaleFactorName("WGra_SF");
|
|
wGraDataPoint.setSunSpecDataType("uint16");
|
|
wGraDataPoint.setAccess(SunSpecDataPoint::AccessReadWrite);
|
|
wGraDataPoint.setByteOrder(m_byteOrder);
|
|
m_dataPoints.insert(wGraDataPoint.name(), wGraDataPoint);
|
|
|
|
SunSpecDataPoint hzStrDataPoint;
|
|
hzStrDataPoint.setName("HzStr");
|
|
hzStrDataPoint.setLabel("HzStr");
|
|
hzStrDataPoint.setDescription("The frequency deviation from nominal frequency (ECPNomHz) at which a snapshot of the instantaneous power output is taken to act as the CAPPED power level (PM) and above which reduction in power output occurs.");
|
|
hzStrDataPoint.setUnits("Hz");
|
|
hzStrDataPoint.setMandatory(true);
|
|
hzStrDataPoint.setSize(1);
|
|
hzStrDataPoint.setAddressOffset(3);
|
|
hzStrDataPoint.setBlockOffset(1);
|
|
hzStrDataPoint.setScaleFactorName("HzStrStop_SF");
|
|
hzStrDataPoint.setSunSpecDataType("int16");
|
|
hzStrDataPoint.setAccess(SunSpecDataPoint::AccessReadWrite);
|
|
hzStrDataPoint.setByteOrder(m_byteOrder);
|
|
m_dataPoints.insert(hzStrDataPoint.name(), hzStrDataPoint);
|
|
|
|
SunSpecDataPoint hzStopDataPoint;
|
|
hzStopDataPoint.setName("HzStop");
|
|
hzStopDataPoint.setLabel("HzStop");
|
|
hzStopDataPoint.setDescription("The frequency deviation from nominal frequency (ECPNomHz) at which curtailed power output may return to normal and the cap on the power level value is removed.");
|
|
hzStopDataPoint.setUnits("Hz");
|
|
hzStopDataPoint.setMandatory(true);
|
|
hzStopDataPoint.setSize(1);
|
|
hzStopDataPoint.setAddressOffset(4);
|
|
hzStopDataPoint.setBlockOffset(2);
|
|
hzStopDataPoint.setScaleFactorName("HzStrStop_SF");
|
|
hzStopDataPoint.setSunSpecDataType("int16");
|
|
hzStopDataPoint.setAccess(SunSpecDataPoint::AccessReadWrite);
|
|
hzStopDataPoint.setByteOrder(m_byteOrder);
|
|
m_dataPoints.insert(hzStopDataPoint.name(), hzStopDataPoint);
|
|
|
|
SunSpecDataPoint hysEnaDataPoint;
|
|
hysEnaDataPoint.setName("HysEna");
|
|
hysEnaDataPoint.setLabel("HysEna");
|
|
hysEnaDataPoint.setDescription("Enable hysteresis");
|
|
hysEnaDataPoint.setMandatory(true);
|
|
hysEnaDataPoint.setSize(1);
|
|
hysEnaDataPoint.setAddressOffset(5);
|
|
hysEnaDataPoint.setBlockOffset(3);
|
|
hysEnaDataPoint.setSunSpecDataType("bitfield16");
|
|
hysEnaDataPoint.setAccess(SunSpecDataPoint::AccessReadWrite);
|
|
hysEnaDataPoint.setByteOrder(m_byteOrder);
|
|
m_dataPoints.insert(hysEnaDataPoint.name(), hysEnaDataPoint);
|
|
|
|
SunSpecDataPoint modEnaDataPoint;
|
|
modEnaDataPoint.setName("ModEna");
|
|
modEnaDataPoint.setLabel("ModEna");
|
|
modEnaDataPoint.setDescription("Is Parameterized Frequency-Watt control active.");
|
|
modEnaDataPoint.setMandatory(true);
|
|
modEnaDataPoint.setSize(1);
|
|
modEnaDataPoint.setAddressOffset(6);
|
|
modEnaDataPoint.setBlockOffset(4);
|
|
modEnaDataPoint.setSunSpecDataType("bitfield16");
|
|
modEnaDataPoint.setAccess(SunSpecDataPoint::AccessReadWrite);
|
|
modEnaDataPoint.setByteOrder(m_byteOrder);
|
|
m_dataPoints.insert(modEnaDataPoint.name(), modEnaDataPoint);
|
|
|
|
SunSpecDataPoint hzStopWGraDataPoint;
|
|
hzStopWGraDataPoint.setName("HzStopWGra");
|
|
hzStopWGraDataPoint.setLabel("HzStopWGra");
|
|
hzStopWGraDataPoint.setDescription("The maximum time-based rate of change at which power output returns to normal after having been capped by an over frequency event.");
|
|
hzStopWGraDataPoint.setUnits("% WMax/min");
|
|
hzStopWGraDataPoint.setSize(1);
|
|
hzStopWGraDataPoint.setAddressOffset(7);
|
|
hzStopWGraDataPoint.setBlockOffset(5);
|
|
hzStopWGraDataPoint.setScaleFactorName("RmpIncDec_SF");
|
|
hzStopWGraDataPoint.setSunSpecDataType("uint16");
|
|
hzStopWGraDataPoint.setAccess(SunSpecDataPoint::AccessReadWrite);
|
|
hzStopWGraDataPoint.setByteOrder(m_byteOrder);
|
|
m_dataPoints.insert(hzStopWGraDataPoint.name(), hzStopWGraDataPoint);
|
|
|
|
SunSpecDataPoint wGraSfDataPoint;
|
|
wGraSfDataPoint.setName("WGra_SF");
|
|
wGraSfDataPoint.setLabel("WGra_SF");
|
|
wGraSfDataPoint.setDescription("Scale factor for output gradient.");
|
|
wGraSfDataPoint.setSize(1);
|
|
wGraSfDataPoint.setAddressOffset(8);
|
|
wGraSfDataPoint.setBlockOffset(6);
|
|
wGraSfDataPoint.setSunSpecDataType("sunssf");
|
|
wGraSfDataPoint.setByteOrder(m_byteOrder);
|
|
m_dataPoints.insert(wGraSfDataPoint.name(), wGraSfDataPoint);
|
|
|
|
SunSpecDataPoint hzStrStopSfDataPoint;
|
|
hzStrStopSfDataPoint.setName("HzStrStop_SF");
|
|
hzStrStopSfDataPoint.setLabel("HzStrStop_SF");
|
|
hzStrStopSfDataPoint.setDescription("Scale factor for frequency deviations.");
|
|
hzStrStopSfDataPoint.setSize(1);
|
|
hzStrStopSfDataPoint.setAddressOffset(9);
|
|
hzStrStopSfDataPoint.setBlockOffset(7);
|
|
hzStrStopSfDataPoint.setSunSpecDataType("sunssf");
|
|
hzStrStopSfDataPoint.setByteOrder(m_byteOrder);
|
|
m_dataPoints.insert(hzStrStopSfDataPoint.name(), hzStrStopSfDataPoint);
|
|
|
|
SunSpecDataPoint rmpIncDecSfDataPoint;
|
|
rmpIncDecSfDataPoint.setName("RmpIncDec_SF");
|
|
rmpIncDecSfDataPoint.setLabel("RmpIncDec_SF");
|
|
rmpIncDecSfDataPoint.setDescription("Scale factor for increment and decrement ramps.");
|
|
rmpIncDecSfDataPoint.setSize(1);
|
|
rmpIncDecSfDataPoint.setAddressOffset(10);
|
|
rmpIncDecSfDataPoint.setBlockOffset(8);
|
|
rmpIncDecSfDataPoint.setSunSpecDataType("sunssf");
|
|
rmpIncDecSfDataPoint.setByteOrder(m_byteOrder);
|
|
m_dataPoints.insert(rmpIncDecSfDataPoint.name(), rmpIncDecSfDataPoint);
|
|
|
|
SunSpecDataPoint padDataPoint;
|
|
padDataPoint.setName("Pad");
|
|
padDataPoint.setSize(1);
|
|
padDataPoint.setAddressOffset(11);
|
|
padDataPoint.setBlockOffset(9);
|
|
padDataPoint.setSunSpecDataType("pad");
|
|
padDataPoint.setByteOrder(m_byteOrder);
|
|
m_dataPoints.insert(padDataPoint.name(), padDataPoint);
|
|
|
|
}
|
|
|
|
void SunSpecFreqWattParamModel::processBlockData()
|
|
{
|
|
// Scale factors
|
|
if (m_dataPoints.value("WGra_SF").isValid())
|
|
m_wGraSf = m_dataPoints.value("WGra_SF").toInt16();
|
|
|
|
if (m_dataPoints.value("HzStrStop_SF").isValid())
|
|
m_hzStrStopSf = m_dataPoints.value("HzStrStop_SF").toInt16();
|
|
|
|
if (m_dataPoints.value("RmpIncDec_SF").isValid())
|
|
m_rmpIncDecSf = m_dataPoints.value("RmpIncDec_SF").toInt16();
|
|
|
|
|
|
// Update properties according to the data point type
|
|
if (m_dataPoints.value("WGra").isValid())
|
|
m_wGra = m_dataPoints.value("WGra").toFloatWithSSF(m_wGraSf);
|
|
|
|
if (m_dataPoints.value("HzStr").isValid())
|
|
m_hzStr = m_dataPoints.value("HzStr").toFloatWithSSF(m_hzStrStopSf);
|
|
|
|
if (m_dataPoints.value("HzStop").isValid())
|
|
m_hzStop = m_dataPoints.value("HzStop").toFloatWithSSF(m_hzStrStopSf);
|
|
|
|
if (m_dataPoints.value("HysEna").isValid())
|
|
m_hysEna = static_cast<HysenaFlags>(m_dataPoints.value("HysEna").toUInt16());
|
|
|
|
if (m_dataPoints.value("ModEna").isValid())
|
|
m_modEna = static_cast<ModenaFlags>(m_dataPoints.value("ModEna").toUInt16());
|
|
|
|
if (m_dataPoints.value("HzStopWGra").isValid())
|
|
m_hzStopWGra = m_dataPoints.value("HzStopWGra").toFloatWithSSF(m_rmpIncDecSf);
|
|
|
|
if (m_dataPoints.value("WGra_SF").isValid())
|
|
m_wGraSf = m_dataPoints.value("WGra_SF").toInt16();
|
|
|
|
if (m_dataPoints.value("HzStrStop_SF").isValid())
|
|
m_hzStrStopSf = m_dataPoints.value("HzStrStop_SF").toInt16();
|
|
|
|
if (m_dataPoints.value("RmpIncDec_SF").isValid())
|
|
m_rmpIncDecSf = m_dataPoints.value("RmpIncDec_SF").toInt16();
|
|
|
|
if (m_dataPoints.value("Pad").isValid())
|
|
m_pad = m_dataPoints.value("Pad").toUInt16();
|
|
|
|
|
|
qCDebug(dcSunSpecModelData()) << this;
|
|
}
|
|
|
|
QDebug operator<<(QDebug debug, SunSpecFreqWattParamModel *model)
|
|
{
|
|
debug.nospace().noquote() << "SunSpecFreqWattParamModel(Model: " << model->modelId() << ", Register: " << model->modbusStartRegister() << ", Length: " << model->modelLength() << ")\n";
|
|
debug.nospace().noquote() << " - " << model->dataPoints().value("WGra") << "-->";
|
|
if (model->dataPoints().value("WGra").isValid()) {
|
|
debug.nospace().noquote() << model->wGra() << "\n";
|
|
} else {
|
|
debug.nospace().noquote() << "NaN\n";
|
|
}
|
|
|
|
debug.nospace().noquote() << " - " << model->dataPoints().value("HzStr") << "-->";
|
|
if (model->dataPoints().value("HzStr").isValid()) {
|
|
debug.nospace().noquote() << model->hzStr() << "\n";
|
|
} else {
|
|
debug.nospace().noquote() << "NaN\n";
|
|
}
|
|
|
|
debug.nospace().noquote() << " - " << model->dataPoints().value("HzStop") << "-->";
|
|
if (model->dataPoints().value("HzStop").isValid()) {
|
|
debug.nospace().noquote() << model->hzStop() << "\n";
|
|
} else {
|
|
debug.nospace().noquote() << "NaN\n";
|
|
}
|
|
|
|
debug.nospace().noquote() << " - " << model->dataPoints().value("HysEna") << "-->";
|
|
if (model->dataPoints().value("HysEna").isValid()) {
|
|
debug.nospace().noquote() << model->hysEna() << "\n";
|
|
} else {
|
|
debug.nospace().noquote() << "NaN\n";
|
|
}
|
|
|
|
debug.nospace().noquote() << " - " << model->dataPoints().value("ModEna") << "-->";
|
|
if (model->dataPoints().value("ModEna").isValid()) {
|
|
debug.nospace().noquote() << model->modEna() << "\n";
|
|
} else {
|
|
debug.nospace().noquote() << "NaN\n";
|
|
}
|
|
|
|
debug.nospace().noquote() << " - " << model->dataPoints().value("HzStopWGra") << "-->";
|
|
if (model->dataPoints().value("HzStopWGra").isValid()) {
|
|
debug.nospace().noquote() << model->hzStopWGra() << "\n";
|
|
} else {
|
|
debug.nospace().noquote() << "NaN\n";
|
|
}
|
|
|
|
debug.nospace().noquote() << " - " << model->dataPoints().value("Pad") << "-->";
|
|
if (model->dataPoints().value("Pad").isValid()) {
|
|
debug.nospace().noquote() << model->pad() << "\n";
|
|
} else {
|
|
debug.nospace().noquote() << "NaN\n";
|
|
}
|
|
|
|
|
|
return debug.space().quote();
|
|
}
|