/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Copyright 2013 - 2020, nymea GmbH * Contact: contact@nymea.io * * This file 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 . * * 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 "sunspecdatapoint.h" #include #include SunSpecDataPoint::SunSpecDataPoint() { } QString SunSpecDataPoint::name() const { return m_name; } void SunSpecDataPoint::setName(const QString &name) { m_name = name; } QString SunSpecDataPoint::label() const { return m_label; } void SunSpecDataPoint::setLabel(const QString &label) { m_label = label; } QString SunSpecDataPoint::description() const { return m_description; } void SunSpecDataPoint::setDescription(const QString &description) { m_description = description; } QString SunSpecDataPoint::detail() const { return m_detail; } void SunSpecDataPoint::setDetail(const QString &detail) { m_detail = detail; } QString SunSpecDataPoint::units() const { return m_units; } void SunSpecDataPoint::setUnits(const QString &units) { m_units = units; } bool SunSpecDataPoint::mandatory() const { return m_mandatory; } void SunSpecDataPoint::setMandatory(bool mandatory) { m_mandatory = mandatory; } SunSpecDataPoint::Access SunSpecDataPoint::access() const { return m_access; } void SunSpecDataPoint::setAccess(Access access) { m_access = access; } quint16 SunSpecDataPoint::addressOffset() const { return m_addressOffset; } void SunSpecDataPoint::setAddressOffset(quint16 addressOffset) { m_addressOffset = addressOffset; } quint16 SunSpecDataPoint::blockOffset() const { return m_blockOffset; } void SunSpecDataPoint::setBlockOffset(quint16 blockOffset) { m_blockOffset = blockOffset; } QString SunSpecDataPoint::sunSpecDataType() const { return m_sunSpecDataType; } void SunSpecDataPoint::setSunSpecDataType(const QString &sunSpecDataType) { m_sunSpecDataType = sunSpecDataType; setDataType(SunSpecDataPoint::stringToDataType(m_sunSpecDataType)); } SunSpecDataPoint::DataType SunSpecDataPoint::dataType() const { return m_dataType; } void SunSpecDataPoint::setDataType(DataType dataType) { m_dataType = dataType; } SunSpecDataPoint::ByteOrder SunSpecDataPoint::byteOrder() const { return m_byteOrder; } void SunSpecDataPoint::setByteOrder(ByteOrder byteOrder) { m_byteOrder = byteOrder; } int SunSpecDataPoint::size() const { return m_size; } void SunSpecDataPoint::setSize(int size) { m_size = size; } QString SunSpecDataPoint::scaleFactorName() const { return m_scaleFactorName; } void SunSpecDataPoint::setScaleFactorName(const QString &scaleFactorName) { m_scaleFactorName = scaleFactorName; } QVector SunSpecDataPoint::rawData() const { return m_rawData; } void SunSpecDataPoint::setRawData(const QVector &rawData) { m_rawData = rawData; } bool SunSpecDataPoint::isValid() const { // TODO: verify if value is not invalid code if (m_rawData.isEmpty()) return false; bool valid = true; switch (m_dataType) { case DataType::Pad: case DataType::Int16: case DataType::ScaleFactor: if (toUInt16() == 0x8000) valid = false; break; case DataType::Int32: if (toUInt32() == 0x80000000) valid = false; break; case DataType::Int64: if (toUInt64() == 0x8000000000000000) valid = false; break; case DataType::Raw16: if (toUInt16() == 0x0000) valid = false; break; case DataType::Acc16: if (toUInt16() == 0x0000) valid = false; break; case DataType::IpV4Address: case DataType::Acc32: if (toUInt32() == 0x00000000) valid = false; break; case DataType::BitField16: case DataType::Enum16: case DataType::UInt16: if (toUInt16() == 0xFFFF) valid = false; break; case DataType::BitField32: case DataType::Enum32: case DataType::UInt32: if (toUInt32() == 0xFFFFFFFF) valid = false; break; case DataType::IpV6Address: case DataType::Acc64: if (toUInt64() == 0x0000000000000000) valid = false; break; break; case DataType::BitField64: break; case DataType::Float32: if (toUInt32() == 0x7FC00000) valid = false; break; case DataType::Float64: if (toUInt64() == 0x7FC0000000000000) valid = false; break; case DataType::String: { bool isNull = true; for (int i = 0; i < m_rawData.count(); i++) { if (m_rawData.at(i) != 0x0000) { isNull = false; valid = true; break; } } if (isNull) valid = false; break; } case DataType::EUI48: case DataType::Group: case DataType::Sync: break; } return valid; } SunSpecDataPoint::DataType SunSpecDataPoint::stringToDataType(const QString &typString) { DataType dataType = DataType::UInt16; if (typString == "int16") { dataType = DataType::Int16; } else if (typString == "int32") { dataType = DataType::Int32; } else if (typString == "int64") { dataType = DataType::Int64; } else if (typString == "uint16") { dataType = DataType::UInt16; } else if (typString == "raw16") { dataType = DataType::Raw16; } else if (typString == "uint32") { dataType = DataType::UInt32; } else if (typString == "acc16") { dataType = DataType::Acc16; } else if (typString == "acc32") { dataType = DataType::Acc32; } else if (typString == "acc64") { dataType = DataType::Acc64; } else if (typString == "bitfield16") { dataType = DataType::BitField16; } else if (typString == "bitfield32") { dataType = DataType::BitField32; } else if (typString == "bitfield64") { dataType = DataType::BitField64; } else if (typString == "enum16") { dataType = DataType::Enum16; } else if (typString == "enum32") { dataType = DataType::Enum32; } else if (typString == "float32") { dataType = DataType::Float32; } else if (typString == "float64") { dataType = DataType::Float64; } else if (typString == "string") { dataType = DataType::String; } else if (typString == "sunssf") { dataType = DataType::ScaleFactor; } else if (typString == "pad") { dataType = DataType::Pad; } else if (typString == "ipaddr") { dataType = DataType::IpV4Address; } else if (typString == "ipv6addr") { dataType = DataType::IpV6Address; } else if (typString == "eui48") { dataType = DataType::EUI48; } else if (typString == "group") { dataType = DataType::Group; } else if (typString == "sync") { dataType = DataType::Sync; } return dataType; } quint16 SunSpecDataPoint::toUInt16() const { return SunSpecDataPoint::convertToUInt16(m_rawData); } qint16 SunSpecDataPoint::toInt16() const { return SunSpecDataPoint::convertToInt16(m_rawData); } quint32 SunSpecDataPoint::toUInt32() const { return SunSpecDataPoint::convertToUInt32(m_rawData, m_byteOrder); } qint32 SunSpecDataPoint::toInt32() const { return SunSpecDataPoint::convertToInt32(m_rawData, m_byteOrder); } quint64 SunSpecDataPoint::toUInt64() const { return SunSpecDataPoint::convertToUInt64(m_rawData, m_byteOrder); } qint64 SunSpecDataPoint::toInt64() const { return SunSpecDataPoint::convertToInt64(m_rawData, m_byteOrder); } QString SunSpecDataPoint::toString() const { return SunSpecDataPoint::convertToString(m_rawData); } float SunSpecDataPoint::toFloat() const { Q_ASSERT_X(m_dataType == Float32, "SunSpecDataPoint", "invalid raw data size for converting value to float"); return SunSpecDataPoint::convertToFloat32(m_rawData, m_byteOrder); } float SunSpecDataPoint::toFloatWithSSF(qint16 scaleFactor) const { float value = 0; switch (m_dataType) { case Acc16: case UInt16: value = toUInt16() * pow(10, scaleFactor); break; case Int16: value = toInt16() * pow(10, scaleFactor); break; case Acc32: case UInt32: value = toUInt32() * pow(10, scaleFactor); break; case Int32: value = toInt32() * pow(10, scaleFactor); break; default: Q_ASSERT_X(false, "SunSpecDataPoint", QString("unhandled data type for converting float with scale factor %1").arg(dataType()).toLatin1()); break; } return value; } double SunSpecDataPoint::toDouble() const { Q_ASSERT_X(m_dataType == Float64, "SunSpecDataPoint", "invalid raw data size for converting value to double"); return SunSpecDataPoint::convertToFloat64(m_rawData, m_byteOrder); } QString SunSpecDataPoint::registersToString(const QVector ®isters) { QStringList valueStrings; for (int i = 0; i < registers.count(); i++) { QString hexString(QStringLiteral("0x%1")); valueStrings.append(hexString.arg(registers.at(i), 4, 16, QLatin1Char('0'))); } QString registersString = "(" + valueStrings.join(", ") + ")"; return registersString; } quint16 SunSpecDataPoint::convertToUInt16(const QVector ®isters) { Q_ASSERT_X(registers.count() == 1, "SunSpecDataPoint", "invalid raw data size for converting value to quint16"); return registers.at(0); } qint16 SunSpecDataPoint::convertToInt16(const QVector ®isters) { Q_ASSERT_X(registers.count() == 1, "SunSpecDataPoint", "invalid raw data size for converting value to qint16"); return static_cast(registers.at(0)); } quint32 SunSpecDataPoint::convertToUInt32(const QVector ®isters, ByteOrder byteOrder) { Q_ASSERT_X(registers.count() == 2, "SunSpecDataPoint", "invalid raw data size for converting value to quint32"); QByteArray data; QDataStream inputStream(&data, QIODevice::WriteOnly); if (byteOrder == ByteOrderBigEndian) { inputStream << registers.at(0); inputStream << registers.at(1); } else { inputStream << registers.at(1); inputStream << registers.at(0); } QDataStream outputStream(&data, QIODevice::ReadOnly); quint32 result = 0; outputStream >> result; return result; } qint32 SunSpecDataPoint::convertToInt32(const QVector ®isters, ByteOrder byteOrder) { Q_ASSERT_X(registers.count() == 2, "SunSpecDataPoint", "invalid raw data size for converting value to quint32"); QByteArray data; QDataStream inputStream(&data, QIODevice::WriteOnly); if (byteOrder == ByteOrderBigEndian) { inputStream << registers.at(0); inputStream << registers.at(1); } else { inputStream << registers.at(1); inputStream << registers.at(0); } QDataStream outputStream(&data, QIODevice::ReadOnly); qint32 result = 0; outputStream >> result; return result; } quint64 SunSpecDataPoint::convertToUInt64(const QVector ®isters, ByteOrder byteOrder) { Q_ASSERT_X(registers.count() == 4, "SunSpecDataPoint", "invalid raw data size for converting value to quint64"); QByteArray data; QDataStream inputStream(&data, QIODevice::WriteOnly); if (byteOrder == ByteOrderBigEndian) { inputStream << registers.at(0); inputStream << registers.at(1); inputStream << registers.at(2); inputStream << registers.at(3); } else { inputStream << registers.at(3); inputStream << registers.at(2); inputStream << registers.at(1); inputStream << registers.at(0); } QDataStream outputStream(&data, QIODevice::ReadOnly); quint64 result = 0; outputStream >> result; return result; } qint64 SunSpecDataPoint::convertToInt64(const QVector ®isters, ByteOrder byteOrder) { Q_ASSERT_X(registers.count() == 4, "SunSpecDataPoint", "invalid raw data size for converting value to qint64"); QByteArray data; QDataStream inputStream(&data, QIODevice::WriteOnly); if (byteOrder == ByteOrderBigEndian) { inputStream << registers.at(0); inputStream << registers.at(1); inputStream << registers.at(2); inputStream << registers.at(3); } else { inputStream << registers.at(3); inputStream << registers.at(2); inputStream << registers.at(1); inputStream << registers.at(0); } QDataStream outputStream(&data, QIODevice::ReadOnly); qint64 result = 0; outputStream >> result; return result; } QString SunSpecDataPoint::convertToString(const QVector ®isters) { QByteArray bytes; QDataStream stream(&bytes, QIODevice::WriteOnly); for (int i = 0; i < registers.count(); i++) { stream << registers.at(i); } return QString::fromUtf8(bytes).trimmed(); } float SunSpecDataPoint::convertToFloat32(const QVector ®isters, ByteOrder byteOrder) { Q_ASSERT_X(registers.count() == 2, "SunSpecDataPoint", "invalid raw data size for converting value to float32"); quint32 rawValue = SunSpecDataPoint::convertToUInt32(registers, byteOrder); float value = 0; memcpy(&value, &rawValue, sizeof(quint32)); return value; } double SunSpecDataPoint::convertToFloat64(const QVector ®isters, ByteOrder byteOrder) { Q_ASSERT_X(registers.count() == 4, "SunSpecDataPoint", "invalid raw data size for converting value to float64"); quint64 rawValue = SunSpecDataPoint::convertToUInt64(registers, byteOrder); double value = 0; memcpy(&value, &rawValue, sizeof(quint64)); return value; } QVector SunSpecDataPoint::convertFromUInt16(quint16 value) { return QVector() << value; } QVector SunSpecDataPoint::convertFromInt16(qint16 value) { return SunSpecDataPoint::convertFromUInt16(static_cast(value)); } QVector SunSpecDataPoint::convertFromUInt32(quint32 value, ByteOrder byteOrder) { QByteArray data; QDataStream inputStream(&data, QIODevice::WriteOnly); inputStream << value; QDataStream outputStream(&data, QIODevice::ReadOnly); QVector values; for (int i = 0; i < 2; i++) { quint16 registerValue = 0; outputStream >> registerValue; if (byteOrder == ByteOrderBigEndian) { values.append(registerValue); } else { values.prepend(registerValue); } } return values; } QVector SunSpecDataPoint::convertFromInt32(qint32 value, ByteOrder byteOrder) { return SunSpecDataPoint::convertFromUInt32(static_cast(value), byteOrder); } QVector SunSpecDataPoint::convertFromUInt64(quint64 value, ByteOrder byteOrder) { QByteArray data; QDataStream inputStream(&data, QIODevice::WriteOnly); inputStream << value; QDataStream outputStream(&data, QIODevice::ReadOnly); QVector values; for (int i = 0; i < 4; i++) { quint16 registerValue = 0; outputStream >> registerValue; if (byteOrder == ByteOrderBigEndian) { values.append(registerValue); } else { values.prepend(registerValue); } } return values; } QVector SunSpecDataPoint::convertFromInt64(qint64 value, ByteOrder byteOrder) { QByteArray data; QDataStream inputStream(&data, QIODevice::WriteOnly); inputStream << value; QDataStream outputStream(&data, QIODevice::ReadOnly); QVector values; for (int i = 0; i < 4; i++) { quint16 registerValue = 0; outputStream >> registerValue; if (byteOrder == ByteOrderBigEndian) { values.append(registerValue); } else { values.prepend(registerValue); } } return values; } QVector SunSpecDataPoint::convertFromString(const QString &value, quint16 stringLength) { Q_ASSERT_X(value.length() <= stringLength, "SunSpecDataPoint", "cannot convert a string which is bigger than the desired register vector."); QByteArray data = value.toLatin1() + QByteArray('\0', stringLength - value.count()); QDataStream stream(&data, QIODevice::ReadOnly); QVector values; for (int i = 0; i < stringLength; i++) { quint16 registerValue = 0; stream >> registerValue; values.append(registerValue); } return values; } QVector SunSpecDataPoint::convertFromFloatWithSSF(float value, qint16 scaleFactor, DataType dataType, ByteOrder byteOrder) { QVector rawData; switch (dataType) { case Acc16: case UInt16: { quint16 rawValue = static_cast(value * pow(10, -1 * scaleFactor)); rawData << rawValue; break; } case Int16: { quint16 rawValue = static_cast(value * pow(10, -1 * scaleFactor)); rawData << rawValue; break; } case Acc32: case UInt32: { quint32 rawValue = static_cast(value * pow(10, -1 * scaleFactor)); rawData = SunSpecDataPoint::convertFromUInt32(rawValue, byteOrder); break; } case Int32: { qint32 rawValue = static_cast(value * pow(10, -1 * scaleFactor)); rawData = SunSpecDataPoint::convertFromInt32(rawValue, byteOrder); break; } default: Q_ASSERT_X(false, "SunSpecDataPoint", QString("unhandled data type for converting from float with scale factor using data type %1").arg(dataType).toLatin1()); break; } return rawData; } QVector SunSpecDataPoint::convertFromFloat32(float value, ByteOrder byteOrder) { quint32 rawValue = 0; memcpy(&rawValue, &value, sizeof(float)); return SunSpecDataPoint::convertFromUInt32(rawValue, byteOrder); } QVector SunSpecDataPoint::convertFromFloat64(double value, ByteOrder byteOrder) { quint64 rawValue = 0; memcpy(&rawValue, &value, sizeof(double)); return SunSpecDataPoint::convertFromUInt64(rawValue, byteOrder); } QDebug operator<<(QDebug debug, const SunSpecDataPoint &dataPoint) { debug.nospace().noquote() << "DataPoint("; if (dataPoint.description().isEmpty()) { debug.nospace().noquote() << dataPoint.name(); } else { debug.nospace().noquote() << dataPoint.description() << ", " << dataPoint.name() << ", "; } if (dataPoint.access() == SunSpecDataPoint::AccessReadWrite) { debug.nospace().noquote() << "RW, "; } else { debug.nospace().noquote() << "R, "; } if (dataPoint.mandatory()) { debug.nospace().noquote() << "M, "; } else { debug.nospace().noquote() << "O, "; } debug.nospace().noquote() << dataPoint.sunSpecDataType(); if (!dataPoint.units().isEmpty()) { debug.nospace().noquote() << ", [" << dataPoint.units() << "]"; } debug.nospace().noquote() << ")"; return debug.space().quote(); }