1 /**************************************************************************** 2 ** 3 ** Copyright (C) 2017 The Qt Company Ltd. 4 ** Contact: http://www.qt.io/licensing/ 5 ** 6 ** This file is part of the QtSerialBus module of the Qt Toolkit. 7 ** 8 ** $QT_BEGIN_LICENSE:LGPL3$ 9 ** Commercial License Usage 10 ** Licensees holding valid commercial Qt licenses may use this file in 11 ** accordance with the commercial license agreement provided with the 12 ** Software or, alternatively, in accordance with the terms contained in 13 ** a written agreement between you and The Qt Company. For licensing terms 14 ** and conditions see http://www.qt.io/terms-conditions. For further 15 ** information use the contact form at http://www.qt.io/contact-us. 16 ** 17 ** GNU Lesser General Public License Usage 18 ** Alternatively, this file may be used under the terms of the GNU Lesser 19 ** General Public License version 3 as published by the Free Software 20 ** Foundation and appearing in the file LICENSE.LGPLv3 included in the 21 ** packaging of this file. Please review the following information to 22 ** ensure the GNU Lesser General Public License version 3 requirements 23 ** will be met: https://www.gnu.org/licenses/lgpl.html. 24 ** 25 ** GNU General Public License Usage 26 ** Alternatively, this file may be used under the terms of the GNU 27 ** General Public License version 2.0 or later as published by the Free 28 ** Software Foundation and appearing in the file LICENSE.GPL included in 29 ** the packaging of this file. Please review the following information to 30 ** ensure the GNU General Public License version 2.0 requirements will be 31 ** met: http://www.gnu.org/licenses/gpl-2.0.html. 32 ** 33 ** $QT_END_LICENSE$ 34 ** 35 ****************************************************************************/ 36 #ifndef QMODBUSPDU_H 37 #define QMODBUSPDU_H 38 39 #include <QtCore/qdatastream.h> 40 #include <QtCore/qmetatype.h> 41 #include <QtCore/qvector.h> 42 #include <QtSerialBus/qtserialbusglobal.h> 43 44 QT_BEGIN_NAMESPACE 45 46 class QModbusPdu 47 { 48 public: 49 enum ExceptionCode { 50 IllegalFunction = 0x01, 51 IllegalDataAddress = 0x02, 52 IllegalDataValue = 0x03, 53 ServerDeviceFailure = 0x04, 54 Acknowledge = 0x05, 55 ServerDeviceBusy = 0x06, 56 NegativeAcknowledge = 0x07, 57 MemoryParityError = 0x08, 58 GatewayPathUnavailable = 0x0A, 59 GatewayTargetDeviceFailedToRespond = 0x0B, 60 ExtendedException = 0xFF, 61 }; 62 63 enum FunctionCode { 64 Invalid = 0x00, 65 ReadCoils = 0x01, 66 ReadDiscreteInputs = 0x02, 67 ReadHoldingRegisters = 0x03, 68 ReadInputRegisters = 0x04, 69 WriteSingleCoil = 0x05, 70 WriteSingleRegister = 0x06, 71 ReadExceptionStatus = 0x07, 72 Diagnostics = 0x08, 73 GetCommEventCounter = 0x0B, 74 GetCommEventLog = 0x0C, 75 WriteMultipleCoils = 0x0F, 76 WriteMultipleRegisters = 0x10, 77 ReportServerId = 0x11, 78 ReadFileRecord = 0x14, 79 WriteFileRecord = 0x15, 80 MaskWriteRegister = 0x16, 81 ReadWriteMultipleRegisters = 0x17, 82 ReadFifoQueue = 0x18, 83 EncapsulatedInterfaceTransport = 0x2B, 84 UndefinedFunctionCode = 0x100 85 }; 86 87 QModbusPdu() = default; 88 virtual ~QModbusPdu() = default; 89 isValid()90 bool isValid() const { 91 return (m_code >= ReadCoils && m_code < UndefinedFunctionCode) 92 && (m_data.size() < 253); 93 } 94 95 static const quint8 ExceptionByte = 0x80; exceptionCode()96 ExceptionCode exceptionCode() const { 97 if (!m_data.size() || !isException()) 98 return ExtendedException; 99 return static_cast<ExceptionCode>(m_data.at(0)); 100 } isException()101 bool isException() const { return m_code & ExceptionByte; } 102 size()103 qint16 size() const { return dataSize() + 1; } dataSize()104 qint16 dataSize() const { return qint16(m_data.size()); } 105 functionCode()106 FunctionCode functionCode() const { 107 return FunctionCode(quint8(m_code) &~ ExceptionByte); 108 } setFunctionCode(FunctionCode code)109 virtual void setFunctionCode(FunctionCode code) { m_code = code; } 110 data()111 QByteArray data() const { return m_data; } setData(const QByteArray & newData)112 void setData(const QByteArray &newData) { m_data = newData; } 113 encodeData(Args...newData)114 template <typename ... Args> void encodeData(Args ... newData) { 115 encode(std::forward<Args>(newData)...); 116 } 117 decodeData(Args &&...newData)118 template <typename ... Args> void decodeData(Args && ... newData) const { 119 decode(std::forward<Args>(newData)...); 120 } 121 122 protected: QModbusPdu(FunctionCode code,const QByteArray & newData)123 QModbusPdu(FunctionCode code, const QByteArray &newData) 124 : m_code(code) 125 , m_data(newData) 126 {} 127 128 QModbusPdu(const QModbusPdu &) = default; 129 QModbusPdu &operator=(const QModbusPdu &) = default; 130 131 template <typename ... Args> QModbusPdu(FunctionCode code,Args...newData)132 QModbusPdu(FunctionCode code, Args ... newData) 133 : m_code(code) 134 { 135 encode(std::forward<Args>(newData)...); 136 } 137 138 private: 139 template <typename T, typename ... Ts> struct IsType { enum { value = false }; }; 140 template <typename T, typename T1, typename ... Ts> struct IsType<T, T1, Ts...> { 141 enum { value = std::is_same<T, T1>::value || IsType<T, Ts...>::value }; 142 }; 143 144 template <typename T> 145 using is_pod = std::integral_constant<bool, std::is_trivial<T>::value && std::is_standard_layout<T>::value>; 146 147 template <typename T> void encode(QDataStream *stream, const T &t) { 148 static_assert(is_pod<T>::value, "Only POD types supported."); 149 static_assert(IsType<T, quint8, quint16>::value, "Only quint8 and quint16 supported."); 150 (*stream) << t; 151 } 152 template <typename T> void decode(QDataStream *stream, T &t) const { 153 static_assert(is_pod<T>::value, "Only POD types supported."); 154 static_assert(IsType<T, quint8 *, quint16 *>::value, "Only quint8* and quint16* supported."); 155 (*stream) >> *t; 156 } 157 template <typename T> void encode(QDataStream *stream, const QVector<T> &vector) { 158 static_assert(is_pod<T>::value, "Only POD types supported."); 159 static_assert(IsType<T, quint8, quint16>::value, "Only quint8 and quint16 supported."); 160 for (int i = 0; i < vector.count(); ++i) 161 (*stream) << vector[i]; 162 } 163 164 template<typename ... Args> void encode(Args ... newData) { 165 m_data.clear(); 166 Q_CONSTEXPR quint32 argCount = sizeof...(Args); 167 if (argCount > 0) { 168 QDataStream stream(&m_data, QIODevice::WriteOnly); 169 char tmp[argCount] = { (encode(&stream, newData), void(), '0')... }; 170 Q_UNUSED(tmp) 171 } 172 } 173 template<typename ... Args> void decode(Args ... newData) const { 174 Q_CONSTEXPR quint32 argCount = sizeof...(Args); 175 if (argCount > 0 && !m_data.isEmpty()) { 176 QDataStream stream(m_data); 177 char tmp[argCount] = { (decode(&stream, newData), void(), '0')... }; 178 Q_UNUSED(tmp) 179 } 180 } 181 182 private: 183 FunctionCode m_code = Invalid; 184 QByteArray m_data; 185 friend class QModbusSerialAdu; 186 }; 187 Q_SERIALBUS_EXPORT QDebug operator<<(QDebug debug, const QModbusPdu &pdu); 188 Q_SERIALBUS_EXPORT QDataStream &operator<<(QDataStream &stream, const QModbusPdu &pdu); 189 190 class QModbusRequest : public QModbusPdu 191 { 192 public: 193 QModbusRequest() = default; 194 QModbusRequest(const QModbusPdu &pdu) 195 : QModbusPdu(pdu) 196 {} 197 198 explicit QModbusRequest(FunctionCode code, const QByteArray &newData = QByteArray()) 199 : QModbusPdu(code, newData) 200 {} 201 202 Q_SERIALBUS_EXPORT static int minimumDataSize(const QModbusRequest &pdu); 203 Q_SERIALBUS_EXPORT static int calculateDataSize(const QModbusRequest &pdu); 204 205 using CalcFuncPtr = decltype(&calculateDataSize); 206 Q_SERIALBUS_EXPORT static void registerDataSizeCalculator(FunctionCode fc, CalcFuncPtr func); 207 208 template <typename ... Args> 209 QModbusRequest(FunctionCode code, Args ... newData) 210 : QModbusPdu(code, newData...) 211 {} 212 }; 213 Q_SERIALBUS_EXPORT QDataStream &operator>>(QDataStream &stream, QModbusRequest &pdu); 214 inline QDataStream &operator<<(QDataStream &stream, const QModbusRequest &pdu) 215 { return stream << static_cast<const QModbusPdu &>(pdu); } 216 217 class QModbusResponse : public QModbusPdu 218 { 219 public: 220 QModbusResponse() = default; 221 QModbusResponse(const QModbusPdu &pdu) 222 : QModbusPdu(pdu) 223 {} 224 225 explicit QModbusResponse(FunctionCode code, const QByteArray &newData = QByteArray()) 226 : QModbusPdu(code, newData) 227 {} 228 229 Q_SERIALBUS_EXPORT static int minimumDataSize(const QModbusResponse &pdu); 230 Q_SERIALBUS_EXPORT static int calculateDataSize(const QModbusResponse &pdu); 231 232 using CalcFuncPtr = decltype(&calculateDataSize); 233 Q_SERIALBUS_EXPORT static void registerDataSizeCalculator(FunctionCode fc, CalcFuncPtr func); 234 235 template <typename ... Args> 236 QModbusResponse(FunctionCode code, Args ... newData) 237 : QModbusPdu(code, newData...) 238 {} 239 }; 240 241 class QModbusExceptionResponse : public QModbusResponse 242 { 243 public: 244 QModbusExceptionResponse() = default; 245 QModbusExceptionResponse(const QModbusPdu &pdu) 246 : QModbusResponse(pdu) 247 {} 248 QModbusExceptionResponse(FunctionCode fc, ExceptionCode ec) 249 : QModbusResponse(FunctionCode(quint8(fc) | ExceptionByte), static_cast<quint8> (ec)) 250 {} 251 252 void setFunctionCode(FunctionCode c) { 253 QModbusPdu::setFunctionCode(FunctionCode(quint8(c) | ExceptionByte)); 254 } 255 void setExceptionCode(ExceptionCode ec) { QModbusPdu::encodeData(quint8(ec)); } 256 }; 257 Q_SERIALBUS_EXPORT QDataStream &operator>>(QDataStream &stream, QModbusResponse &pdu); 258 inline QDataStream &operator<<(QDataStream &stream, const QModbusResponse &pdu) 259 { return stream << static_cast<const QModbusPdu &>(pdu); } 260 261 Q_DECLARE_TYPEINFO(QModbusPdu, Q_MOVABLE_TYPE); 262 Q_DECLARE_TYPEINFO(QModbusPdu::ExceptionCode, Q_PRIMITIVE_TYPE); 263 Q_DECLARE_TYPEINFO(QModbusPdu::FunctionCode, Q_PRIMITIVE_TYPE); 264 265 Q_DECLARE_TYPEINFO(QModbusRequest, Q_MOVABLE_TYPE); 266 Q_DECLARE_TYPEINFO(QModbusResponse, Q_MOVABLE_TYPE); 267 Q_DECLARE_TYPEINFO(QModbusExceptionResponse, Q_MOVABLE_TYPE); 268 269 QT_END_NAMESPACE 270 271 Q_DECLARE_METATYPE(QModbusPdu::ExceptionCode) 272 Q_DECLARE_METATYPE(QModbusPdu::FunctionCode) 273 274 #endif // QMODBUSPDU_H 275