/****************************************************************************** ** ** Copyright (C) 2018 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtKnx module. ** ** $QT_BEGIN_LICENSE:GPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 or (at your option) any later version ** approved by the KDE Free Qt Foundation. The licenses are as published by ** the Free Software Foundation and appearing in the file LICENSE.GPL3 ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** ******************************************************************************/ #include "qknxnetipframe.h" QT_BEGIN_NAMESPACE class QKnxNetIpFramePrivate : public QSharedData { public: QKnxNetIpFrameHeader m_header; QKnxNetIpConnectionHeader m_connectionHeader; QKnxByteArray m_data; }; /*! \class QKnxNetIpFrame \inmodule QtKnx \ingroup qtknx-netip \brief The QKnxNetIpFrame class represents the base for all KNXnet/IP related communication. The communication between KNXnet/IP devices is based on KNXnet/IP frames. A KNXnet/IP frame is a data packet sent over the non-KNX network protocol that consists of a header, comparable to the IP header of an Internet protocol data packet, and optional data of variable length. The type of the KNXnet/IP frame is described by a KNXnet/IP service type identifier in the header. \sa {Qt KNXnet/IP Connection Classes} */ /*! Constructs an empty invalid KNXnet/IP frame object. */ QKnxNetIpFrame::QKnxNetIpFrame() : d_ptr(new QKnxNetIpFramePrivate) {} /*! Destroys the KNXnet/IP frame object and releases all allocated resources. */ QKnxNetIpFrame::~QKnxNetIpFrame() = default; /*! Creates a new KNXnet/IP frame with the given service type \a type and data set to \a data. */ QKnxNetIpFrame::QKnxNetIpFrame(QKnxNetIp::ServiceType type, const QKnxByteArray &data) : QKnxNetIpFrame(type, {}, data) {} /*! Creates a new KNXnet/IP frame with the given service type \a type, connection header set to \a connectionHeader, and data set to \a data. */ QKnxNetIpFrame::QKnxNetIpFrame(QKnxNetIp::ServiceType type, const QKnxNetIpConnectionHeader &connectionHeader, const QKnxByteArray &data) : d_ptr(new QKnxNetIpFramePrivate) { d_ptr->m_connectionHeader = connectionHeader; d_ptr->m_data = data; d_ptr->m_header = { type, quint16(connectionHeader.size() + data.size()) }; } /*! Creates a new KNXnet/IP frame with the given frame header \a header, connection header set to \a connectionHeader, and data set to \a data. */ QKnxNetIpFrame::QKnxNetIpFrame(const QKnxNetIpFrameHeader &header, const QKnxNetIpConnectionHeader &connectionHeader, const QKnxByteArray &data) : d_ptr(new QKnxNetIpFramePrivate) { d_ptr->m_header = header; d_ptr->m_connectionHeader = connectionHeader; d_ptr->m_data = data; } /*! Returns \c true if this is a default constructed frame, otherwise returns \c false. A frame is considered null if it contains no initialized values. */ bool QKnxNetIpFrame::isNull() const { return d_ptr->m_header.isNull() && d_ptr->m_connectionHeader.isNull() && d_ptr->m_data.isNull(); } /*! Returns \c true if the frame contains initialized values and is in itself valid, otherwise returns \c false. A valid KNXnet/IP frame consists of at least a valid header and a size in bytes corresponding to the KNXnet/IP frame header \l QKnxNetIpFrameHeader::totalSize. */ bool QKnxNetIpFrame::isValid() const { if (isNull()) return false; const auto &connectionHeader = d_ptr->m_connectionHeader; auto connHeaderValid = connectionHeader.isNull(); switch (serviceType()) { case QKnxNetIp::ServiceType::TunnelingRequest: case QKnxNetIp::ServiceType::TunnelingAcknowledge: case QKnxNetIp::ServiceType::TunnelingFeatureGet: case QKnxNetIp::ServiceType::TunnelingFeatureSet: case QKnxNetIp::ServiceType::TunnelingFeatureInfo: case QKnxNetIp::ServiceType::TunnelingFeatureResponse: case QKnxNetIp::ServiceType::DeviceConfigurationRequest: case QKnxNetIp::ServiceType::DeviceConfigurationAcknowledge: connHeaderValid = connectionHeader.isValid(); break; default: break; } const auto &header = d_ptr->m_header; auto size = header.size() + connectionHeader.size() + d_ptr->m_data.size(); return header.isValid() && connHeaderValid && header.totalSize() == size && header.dataSize() == (d_ptr->m_data.size() + connectionHeader.size()); } /*! Returns the size in bytes of the KNXnet/IP frame. The size includes the size of the KNXnet/IP frame header, the optional connection header, and the frame's data part. */ quint16 QKnxNetIpFrame::size() const { return d_ptr->m_header.totalSize(); } /*! Returns the size of the KNXnet/IP frame data. The data size excludes the size of the KNXnet/IP frame header and the optional connection header. */ quint16 QKnxNetIpFrame::dataSize() const { return d_ptr->m_header.dataSize() - d_ptr->m_connectionHeader.size(); } /*! Returns the KNXnet/IP frame header. */ QKnxNetIpFrameHeader QKnxNetIpFrame::header() const { return d_ptr->m_header; } /*! Sets the KNXnet/IP frame header to \a header. */ void QKnxNetIpFrame::setHeader(const QKnxNetIpFrameHeader &header) { d_ptr->m_header = header; } /*! Returns the KNXnet/IP frame protocol version. */ quint8 QKnxNetIpFrame::protocolVersion() const { return d_ptr->m_header.protocolVersion(); } /*! Returns the KNXnet/IP frame's service type or QKnxNetIp::Unknown for a default generated frame. */ QKnxNetIp::ServiceType QKnxNetIpFrame::serviceType() const { return d_ptr->m_header.serviceType(); } /*! Sets the KNXnet/IP frame's service type to \a type. */ void QKnxNetIpFrame::setServiceType(QKnxNetIp::ServiceType type) { d_ptr->m_header.setServiceType(type); } /*! Returns the KNXnet/IP frame's connection header. */ QKnxNetIpConnectionHeader QKnxNetIpFrame::connectionHeader() const { return d_ptr->m_connectionHeader; } /*! Sets the KNXnet/IP frame's connection header to \a header and updates the total size accordingly. */ void QKnxNetIpFrame::setConnectionHeader(const QKnxNetIpConnectionHeader &header) { quint16 dataSize = d_ptr->m_header.dataSize() - d_ptr->m_connectionHeader.size(); d_ptr->m_connectionHeader = header; d_ptr->m_header.setDataSize(dataSize + header.size()); } /*! Returns the KNXnet/IP frame's channel ID if a connection header is set. \note By default the function returns null. */ quint8 QKnxNetIpFrame::channelId() const { return d_ptr->m_connectionHeader.channelId(); } /*! Returns the KNXnet/IP frame's sequence number if a connection header is set. \note By default the function returns null. */ quint8 QKnxNetIpFrame::sequenceNumber() const { return d_ptr->m_connectionHeader.sequenceNumber(); } /*! Returns the service type specific value of the KNXnet/IP frame if a connection header is set. \note By default the function returns null. */ quint8 QKnxNetIpFrame::serviceTypeSpecificValue() const { return d_ptr->m_connectionHeader.serviceTypeSpecificValue(); } /*! Returns a byte array with connection type specific header items of the KNXnet/IP frame if a connection header is set. \note The returned array can be empty. */ QKnxByteArray QKnxNetIpFrame::connectionTypeSpecificHeaderItems() const { return d_ptr->m_connectionHeader.connectionTypeSpecificHeaderItems(); } /*! Returns the data part of the KNXnet/IP frame. \note If a connection header is set, it is not considered part of the returned data. */ QKnxByteArray QKnxNetIpFrame::data() const { return d_ptr->m_data; } /*! Returns the data part of the KNXnet/IP frame as constant reference. \note If a connection header is set, it is not considered part of the returned data. */ const QKnxByteArray &QKnxNetIpFrame::constData() const { return d_ptr->m_data; } /*! Sets the data part of the KNXnet/IP frame to \a data and updates the total size accordingly. \note The data part passed may not contain the connection header. */ void QKnxNetIpFrame::setData(const QKnxByteArray &data) { auto dataSize = d_ptr->m_header.dataSize() - d_ptr->m_data.size(); d_ptr->m_data = data; d_ptr->m_header.setDataSize(dataSize + data.size()); } /*! Returns an array of bytes that represent the KNXnet/IP frame. */ QKnxByteArray QKnxNetIpFrame::bytes() const { return d_ptr->m_header.bytes() + d_ptr->m_connectionHeader.bytes() + d_ptr->m_data; } /*! Constructs the KNXnet/IP frame from the byte array \a bytes starting at position \a index inside the array. */ QKnxNetIpFrame QKnxNetIpFrame::fromBytes(const QKnxByteArray &bytes, quint16 index) { auto header = QKnxNetIpFrameHeader::fromBytes(bytes, index); if (!header.isValid()) return {}; index += header.size(); QKnxNetIpConnectionHeader connHeader; switch (header.serviceType()) { case QKnxNetIp::ServiceType::TunnelingRequest: case QKnxNetIp::ServiceType::TunnelingAcknowledge: case QKnxNetIp::ServiceType::TunnelingFeatureGet: case QKnxNetIp::ServiceType::TunnelingFeatureSet: case QKnxNetIp::ServiceType::TunnelingFeatureInfo: case QKnxNetIp::ServiceType::TunnelingFeatureResponse: case QKnxNetIp::ServiceType::DeviceConfigurationRequest: case QKnxNetIp::ServiceType::DeviceConfigurationAcknowledge: { connHeader = QKnxNetIpConnectionHeader::fromBytes(bytes, index); if (!connHeader.isValid()) return {}; index += connHeader.size(); } break; default: break; } const auto dataSize = header.totalSize() - index; if ((bytes.size() - index) < dataSize) return {}; return { header, connHeader, bytes.mid(index, dataSize) }; } /*! Constructs a copy of \a other. */ QKnxNetIpFrame::QKnxNetIpFrame(const QKnxNetIpFrame &other) : d_ptr(other.d_ptr) {} /*! Assigns the specified \a other to this object. */ QKnxNetIpFrame &QKnxNetIpFrame::operator=(const QKnxNetIpFrame &other) { d_ptr = other.d_ptr; return *this; } /*! Move-constructs an object instance, making it point to the same object that \a other was pointing to. */ QKnxNetIpFrame::QKnxNetIpFrame(QKnxNetIpFrame &&other) Q_DECL_NOTHROW : d_ptr(other.d_ptr) { other.d_ptr = Q_NULLPTR; } /*! Move-assigns \a other to this object instance. */ QKnxNetIpFrame &QKnxNetIpFrame::operator=(QKnxNetIpFrame &&other) Q_DECL_NOTHROW { swap(other); return *this; } /*! Swaps \a other with this object. This operation is very fast and never fails. */ void QKnxNetIpFrame::swap(QKnxNetIpFrame &other) Q_DECL_NOTHROW { d_ptr.swap(other.d_ptr); } /*! Returns \c true if this object and the given \a other are equal; otherwise returns \c false. */ bool QKnxNetIpFrame::operator==(const QKnxNetIpFrame &other) const { return d_ptr == other.d_ptr || [&]() -> bool { return d_ptr->m_header == other.d_ptr->m_header && d_ptr->m_connectionHeader == other.d_ptr->m_connectionHeader && d_ptr->m_data == other.d_ptr->m_data; }(); } /*! Returns \c true if this object and the given \a other are not equal; otherwise returns \c false. */ bool QKnxNetIpFrame::operator!=(const QKnxNetIpFrame &other) const { return !operator==(other); } /*! \internal */ QKnxNetIpFrame::QKnxNetIpFrame(QKnxNetIpFramePrivate &dd) : d_ptr(new QKnxNetIpFramePrivate(dd)) {} /*! \relates QKnxNetIpFrame Writes the KNXnet/IP \a frame to the \a debug stream. */ QDebug operator<<(QDebug debug, const QKnxNetIpFrame &frame) { QDebugStateSaver _(debug); return debug.nospace().noquote() << "0x" << frame.bytes().toHex(); } QT_END_NAMESPACE