1// Copyright (C) 2017 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3#ifndef QMODBUSPDU_H
4#define QMODBUSPDU_H
5
6#include <QtCore/qdatastream.h>
7#include <QtCore/qiodevice.h>
8#include <QtCore/qlist.h>
9#include <QtCore/qmetatype.h>
10#include <QtSerialBus/qtserialbusglobal.h>
11
12QT_BEGIN_NAMESPACE
13
14class Q_SERIALBUS_EXPORT QModbusPdu
15{
16public:
17 enum ExceptionCode {
18 IllegalFunction = 0x01,
19 IllegalDataAddress = 0x02,
20 IllegalDataValue = 0x03,
21 ServerDeviceFailure = 0x04,
22 Acknowledge = 0x05,
23 ServerDeviceBusy = 0x06,
24 NegativeAcknowledge = 0x07,
25 MemoryParityError = 0x08,
26 GatewayPathUnavailable = 0x0A,
27 GatewayTargetDeviceFailedToRespond = 0x0B,
28 ExtendedException = 0xFF,
29 };
30
31 enum FunctionCode {
32 Invalid = 0x00,
33 ReadCoils = 0x01,
34 ReadDiscreteInputs = 0x02,
35 ReadHoldingRegisters = 0x03,
36 ReadInputRegisters = 0x04,
37 WriteSingleCoil = 0x05,
38 WriteSingleRegister = 0x06,
39 ReadExceptionStatus = 0x07,
40 Diagnostics = 0x08,
41 GetCommEventCounter = 0x0B,
42 GetCommEventLog = 0x0C,
43 WriteMultipleCoils = 0x0F,
44 WriteMultipleRegisters = 0x10,
45 ReportServerId = 0x11,
46 ReadFileRecord = 0x14,
47 WriteFileRecord = 0x15,
48 MaskWriteRegister = 0x16,
49 ReadWriteMultipleRegisters = 0x17,
50 ReadFifoQueue = 0x18,
51 EncapsulatedInterfaceTransport = 0x2B,
52 UndefinedFunctionCode = 0x100
53 };
54
55 QModbusPdu() = default;
56 virtual ~QModbusPdu();
57
58 bool isValid() const {
59 return (m_code >= ReadCoils && m_code < UndefinedFunctionCode)
60 && (m_data.size() < 253);
61 }
62
63 static const quint8 ExceptionByte = 0x80;
64 ExceptionCode exceptionCode() const {
65 if (!m_data.size() || !isException())
66 return ExtendedException;
67 return static_cast<ExceptionCode>(m_data.at(i: 0));
68 }
69 bool isException() const { return m_code & ExceptionByte; }
70
71 qint16 size() const { return dataSize() + 1; }
72 qint16 dataSize() const { return qint16(m_data.size()); }
73
74 FunctionCode functionCode() const {
75 return FunctionCode(quint8(m_code) &~ ExceptionByte);
76 }
77 virtual void setFunctionCode(FunctionCode code) { m_code = code; }
78
79 QByteArray data() const { return m_data; }
80 void setData(const QByteArray &newData) { m_data = newData; }
81
82 template <typename ... Args> void encodeData(Args ... newData) {
83 encode(std::forward<Args>(newData)...);
84 }
85
86 template <typename ... Args> void decodeData(Args && ... newData) const {
87 decode(std::forward<Args>(newData)...);
88 }
89
90protected:
91 QModbusPdu(FunctionCode code, const QByteArray &newData)
92 : m_code(code)
93 , m_data(newData)
94 {}
95
96 QModbusPdu(const QModbusPdu &) = default;
97 QModbusPdu &operator=(const QModbusPdu &) = default;
98
99 template <typename ... Args>
100 QModbusPdu(FunctionCode code, Args ... newData)
101 : m_code(code)
102 {
103 encode(std::forward<Args>(newData)...);
104 }
105
106private:
107 template <typename T, typename ... Ts> struct IsType { enum { value = false }; };
108 template <typename T, typename T1, typename ... Ts> struct IsType<T, T1, Ts...> {
109 enum { value = std::is_same<T, T1>::value || IsType<T, Ts...>::value };
110 };
111
112 template <typename T>
113 using is_pod = std::integral_constant<bool, std::is_trivial<T>::value && std::is_standard_layout<T>::value>;
114
115 template <typename T> void encode(QDataStream *stream, const T &t) {
116 static_assert(is_pod<T>::value, "Only POD types supported.");
117 static_assert(IsType<T, quint8, quint16>::value, "Only quint8 and quint16 supported.");
118 (*stream) << t;
119 }
120 template <typename T> void decode(QDataStream *stream, T &t) const {
121 static_assert(is_pod<T>::value, "Only POD types supported.");
122 static_assert(IsType<T, quint8 *, quint16 *>::value, "Only quint8* and quint16* supported.");
123 (*stream) >> *t;
124 }
125 template<typename T>
126 void encode(QDataStream *stream, const QList<T> &vector)
127 {
128 static_assert(is_pod<T>::value, "Only POD types supported.");
129 static_assert(IsType<T, quint8, quint16>::value, "Only quint8 and quint16 supported.");
130 for (int i = 0; i < vector.size(); ++i)
131 (*stream) << vector[i];
132 }
133
134 template<typename ... Args> void encode(Args ... newData) {
135 m_data.clear();
136 constexpr size_t argCount = sizeof...(Args);
137 if constexpr (argCount > 0) {
138 QDataStream stream(&m_data, QIODevice::WriteOnly);
139 char tmp[argCount] = { (encode(&stream, newData), void(), '0')... };
140 Q_UNUSED(tmp);
141 }
142 }
143 template<typename ... Args> void decode(Args ... newData) const {
144 constexpr size_t argCount = sizeof...(Args);
145 if constexpr (argCount > 0) {
146 if (!m_data.isEmpty()) {
147 QDataStream stream(m_data);
148 char tmp[argCount] = { (decode(&stream, newData), void(), '0')... };
149 Q_UNUSED(tmp);
150 }
151 }
152 }
153
154private:
155 FunctionCode m_code = Invalid;
156 QByteArray m_data;
157 friend class QModbusSerialAdu;
158 friend struct QModbusPduPrivate;
159};
160Q_SERIALBUS_EXPORT QDebug operator<<(QDebug debug, const QModbusPdu &pdu);
161Q_SERIALBUS_EXPORT QDataStream &operator<<(QDataStream &stream, const QModbusPdu &pdu);
162Q_SERIALBUS_EXPORT QDataStream &operator>>(QDataStream &stream, QModbusPdu::FunctionCode &code);
163
164class Q_SERIALBUS_EXPORT QModbusRequest : public QModbusPdu
165{
166public:
167 QModbusRequest() = default;
168 QModbusRequest(const QModbusPdu &pdu)
169 : QModbusPdu(pdu)
170 {}
171
172 explicit QModbusRequest(FunctionCode code, const QByteArray &newData = QByteArray())
173 : QModbusPdu(code, newData)
174 {}
175 ~QModbusRequest() override;
176
177 static int minimumDataSize(const QModbusRequest &pdu);
178 static int calculateDataSize(const QModbusRequest &pdu);
179
180 using CalcFuncPtr = decltype(&calculateDataSize);
181 static void registerDataSizeCalculator(FunctionCode fc, CalcFuncPtr func);
182
183 template <typename ... Args>
184 QModbusRequest(FunctionCode code, Args ... newData)
185 : QModbusPdu(code, newData...)
186 {}
187};
188Q_SERIALBUS_EXPORT QDataStream &operator>>(QDataStream &stream, QModbusRequest &pdu);
189inline QDataStream &operator<<(QDataStream &stream, const QModbusRequest &pdu)
190{ return stream << static_cast<const QModbusPdu &>(pdu); }
191
192class Q_SERIALBUS_EXPORT QModbusResponse : public QModbusPdu
193{
194public:
195 QModbusResponse() = default;
196 QModbusResponse(const QModbusPdu &pdu)
197 : QModbusPdu(pdu)
198 {}
199
200 explicit QModbusResponse(FunctionCode code, const QByteArray &newData = QByteArray())
201 : QModbusPdu(code, newData)
202 {}
203 ~QModbusResponse() override;
204
205 static int minimumDataSize(const QModbusResponse &pdu);
206 static int calculateDataSize(const QModbusResponse &pdu);
207
208 using CalcFuncPtr = decltype(&calculateDataSize);
209 static void registerDataSizeCalculator(FunctionCode fc, CalcFuncPtr func);
210
211 template <typename ... Args>
212 QModbusResponse(FunctionCode code, Args ... newData)
213 : QModbusPdu(code, newData...)
214 {}
215};
216
217class Q_SERIALBUS_EXPORT QModbusExceptionResponse : public QModbusResponse
218{
219public:
220 QModbusExceptionResponse() = default;
221 QModbusExceptionResponse(const QModbusPdu &pdu)
222 : QModbusResponse(pdu)
223 {}
224 QModbusExceptionResponse(FunctionCode fc, ExceptionCode ec)
225 : QModbusResponse(FunctionCode(quint8(fc) | ExceptionByte), static_cast<quint8> (ec))
226 {}
227 ~QModbusExceptionResponse() override;
228
229 void setFunctionCode(FunctionCode c) override {
230 QModbusPdu::setFunctionCode(FunctionCode(quint8(c) | ExceptionByte));
231 }
232 void setExceptionCode(ExceptionCode ec) { QModbusPdu::encodeData(newData: quint8(ec)); }
233};
234Q_SERIALBUS_EXPORT QDataStream &operator>>(QDataStream &stream, QModbusResponse &pdu);
235inline QDataStream &operator<<(QDataStream &stream, const QModbusResponse &pdu)
236{ return stream << static_cast<const QModbusPdu &>(pdu); }
237
238Q_DECLARE_TYPEINFO(QModbusPdu, Q_RELOCATABLE_TYPE);
239Q_DECLARE_TYPEINFO(QModbusPdu::ExceptionCode, Q_PRIMITIVE_TYPE);
240Q_DECLARE_TYPEINFO(QModbusPdu::FunctionCode, Q_PRIMITIVE_TYPE);
241
242Q_DECLARE_TYPEINFO(QModbusRequest, Q_RELOCATABLE_TYPE);
243Q_DECLARE_TYPEINFO(QModbusResponse, Q_RELOCATABLE_TYPE);
244Q_DECLARE_TYPEINFO(QModbusExceptionResponse, Q_RELOCATABLE_TYPE);
245
246QT_END_NAMESPACE
247
248Q_DECLARE_METATYPE(QModbusPdu::ExceptionCode)
249Q_DECLARE_METATYPE(QModbusPdu::FunctionCode)
250
251#endif // QMODBUSPDU_H
252

source code of qtserialbus/src/serialbus/qmodbuspdu.h