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