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
4#include "qmodbusdevice.h"
5#include "qmodbusdevice_p.h"
6#include "qmodbusdataunit.h"
7
8#include <QtCore/qloggingcategory.h>
9
10QT_BEGIN_NAMESPACE
11
12/*!
13 \class QModbusDevice
14 \inmodule QtSerialBus
15 \since 5.8
16
17 \brief The QModbusDevice class is the base class for Modbus classes, \l QModbusServer
18 and \l QModbusClient.
19*/
20
21/*!
22 Constructs a Modbus device with the specified \a parent.
23*/
24QModbusDevice::QModbusDevice(QObject *parent)
25 : QObject(*new QModbusDevicePrivate, parent)
26{
27}
28
29/*!
30 \internal
31*/
32QModbusDevice::QModbusDevice(QModbusDevicePrivate &dd, QObject *parent)
33 : QObject(dd, parent)
34{
35}
36
37/*!
38 Destroys the QModbusDevice instance
39*/
40QModbusDevice::~QModbusDevice()
41{
42}
43
44/*!
45 \enum QModbusDevice::ConnectionParameter
46
47 This enum describes the possible values that can be set for a Modbus device
48 connection.
49
50 The general purpose value (and the associated types) are:
51
52 \value SerialPortNameParameter This parameter holds the serial port used for
53 device communication, e.g. COM1. \c QString
54 \value SerialParityParameter This parameter holds the parity checking mode.
55 \c QSerialPort::Parity
56 \value SerialBaudRateParameter This parameter holds the data baud rate for
57 the communication. \c QSerialPort::BaudRate
58 \value SerialDataBitsParameter This parameter holds the data bits in a frame.
59 \c QSerialPort::DataBits
60 \value SerialStopBitsParameter This parameter holds the number of stop bits in a
61 frame. \c QSerialPort::StopBits
62 \value NetworkPortParameter This parameter holds the network port. \c int
63 \value NetworkAddressParameter This parameter holds the host address for network
64 communication. \c QString
65*/
66
67/*!
68 Returns the value associated with the given connection \a parameter. The
69 returned value can be empty.
70
71 By default the \c QModbusDevice is initialized with some common values. The
72 serial port settings are even parity, a baud rate of 19200 bits per second,
73 eight data bits and one stop bit. The network settings for the host address
74 is set to local host and port to 502.
75
76 \note For a serial connection to succeed, the \l SerialPortNameParameter
77 needs to be set to a valid communication port. The information about valid
78 serial ports can be obtained from \l QSerialPortInfo.
79
80 \note If the device is already connected, the settings are taken into account
81 after reconnecting the device.
82
83 \sa ConnectionParameter
84*/
85QVariant QModbusDevice::connectionParameter(ConnectionParameter parameter) const
86{
87 Q_D(const QModbusDevice);
88 switch (parameter) {
89#if QT_CONFIG(modbus_serialport)
90 case SerialPortNameParameter:
91 return d->m_comPort;
92 case SerialDataBitsParameter:
93 return d->m_dataBits;
94 case SerialParityParameter:
95 return d->m_parity;
96 case SerialStopBitsParameter:
97 return d->m_stopBits;
98 case SerialBaudRateParameter:
99 return d->m_baudRate;
100#endif
101 case NetworkPortParameter:
102 return d->m_networkPort;
103 case NetworkAddressParameter:
104 return d->m_networkAddress;
105 default:
106 break;
107 }
108 return {};
109}
110
111/*!
112 Sets the value of \a parameter to \a value. If the \a parameter already
113 exists, the previous value is overwritten. A active or running connection
114 is not affected by such parameter changes.
115
116 \sa ConnectionParameter
117 \sa connectionParameter()
118*/
119void QModbusDevice::setConnectionParameter(ConnectionParameter parameter, const QVariant &value)
120{
121 Q_D(QModbusDevice);
122 switch (parameter) {
123#if QT_CONFIG(modbus_serialport)
124 case SerialPortNameParameter:
125 d->m_comPort = value.toString();
126 break;
127 case SerialDataBitsParameter:
128 d->m_dataBits = QSerialPort::DataBits(value.toInt());
129 break;
130 case SerialParityParameter:
131 d->m_parity = QSerialPort::Parity(value.toInt());
132 break;
133 case SerialStopBitsParameter:
134 d->m_stopBits = QSerialPort::StopBits(value.toInt());
135 break;
136 case SerialBaudRateParameter:
137 d->m_baudRate = QSerialPort::BaudRate(value.toInt());
138 break;
139#endif
140 case NetworkPortParameter:
141 d->m_networkPort = value.toInt();
142 break;
143 case NetworkAddressParameter:
144 d->m_networkAddress = value.toString();
145 break;
146 default:
147 Q_ASSERT_X(false, "", "Connection parameter not supported.");
148 break;
149 }
150}
151
152/*!
153 \enum QModbusDevice::Error
154 This enum describes all the possible error conditions.
155
156 \value NoError No errors have occurred.
157 \value ReadError An error occurred during a read operation.
158 \value WriteError An error occurred during a write operation.
159 \value ConnectionError An error occurred when attempting to open the
160 backend.
161 \value ConfigurationError An error occurred when attempting to set a
162 configuration parameter.
163 \value TimeoutError A timeout occurred during I/O. An I/O operation
164 did not finish within a given time frame.
165 \value ProtocolError A Modbus specific protocol error occurred.
166 \value ReplyAbortedError The reply was aborted due to a disconnection of
167 the device.
168 \value UnknownError An unknown error occurred.
169 \value [since 6.4] InvalidResponseError An error occurred while parsing the
170 response, or the \l {QModbusPdu::}{FunctionCode}
171 is not supported by the current implementation.
172 In the latter case custom Modbus client
173 implementation can override the
174 \l {QModbusClient::}{processResponse()} and
175 \l {QModbusClient::}{processPrivateResponse()}
176 methods to provide support for needed functions.
177*/
178
179/*!
180 \enum QModbusDevice::State
181 This enum describes all possible device states.
182
183 \value UnconnectedState The device is disconnected.
184 \value ConnectingState The device is being connected.
185 \value ConnectedState The device is connected to the Modbus network.
186 \value ClosingState The device is being closed.
187*/
188
189/*!
190 \since 6.0
191 \enum QModbusDevice::IntermediateError
192
193 This enum describes possible errors that can happen during a full send and
194 receive cycle for a Modbus reply.
195
196 \value ResponseCrcError A Modbus response with a wrong CRC was received.
197 \value ResponseRequestMismatch A Modbus response was received but did not
198 match the open request, probably due to the
199 PDU's function code not matching.
200
201 If any of the above intermediate errors occurred, the frame is likely
202 resent until the maximum number of retries has been reached.
203
204 The list of intermediate errors can be inspected from the \l QModbusReply
205 intermediate errors function.
206
207 \sa QModbusClient::numberOfRetries(), QModbusReply::intermediateErrors()
208*/
209
210/*!
211 \fn QModbusDevice::errorOccurred(QModbusDevice::Error error)
212
213 This signal is emitted when an error of the type, \a error, occurs.
214*/
215
216/*!
217 \fn void QModbusDevice::stateChanged(QModbusDevice::State state)
218
219 This signal is emitted every time the state of the device changes.
220 The new state is represented by \a state.
221
222 \sa setState(), state()
223*/
224
225/*!
226 Connects the device to the Modbus network. Returns \c true if the connection
227 process was successfully initiated; otherwise \c false. Final connection
228 success confirmation requires the \l state() changing to \l QModbusDevice::ConnectedState.
229
230
231 This function calls \l open() as part of its implementation.
232
233 \sa open()
234*/
235bool QModbusDevice::connectDevice()
236{
237 Q_D(QModbusDevice);
238
239 if (d->state != QModbusDevice::UnconnectedState)
240 return false;
241
242 setState(ConnectingState);
243
244 if (!open()) {
245 setState(UnconnectedState);
246 return false;
247 }
248
249 //Connected is set by backend -> might be delayed by event loop
250 return true;
251}
252
253/*!
254 Disconnects the device.
255
256 This function calls \l close() as part of its implementation.
257*/
258void QModbusDevice::disconnectDevice()
259{
260 if (state() == QModbusDevice::UnconnectedState)
261 return;
262
263 setState(QModbusDevice::ClosingState);
264
265 //Unconnected is set by backend -> might be delayed by event loop
266 close();
267}
268
269/*!
270 Sets the state of the device to \a newState. Modbus device implementations
271 must use this function to update the device state.
272*/
273void QModbusDevice::setState(QModbusDevice::State newState)
274{
275 Q_D(QModbusDevice);
276
277 if (newState == d->state)
278 return;
279
280 d->state = newState;
281 emit stateChanged(state: newState);
282}
283
284/*!
285 Returns the current state of the device.
286
287 \sa setState(), stateChanged()
288*/
289QModbusDevice::State QModbusDevice::state() const
290{
291 return d_func()->state;
292}
293
294/*!
295 Sets the error state of the device. ModBus device implementations
296 must use this function in case of an error to set the \a error type and
297 a descriptive \a errorText.
298
299 \sa QModbusDevice::Error
300*/
301void QModbusDevice::setError(const QString &errorText, QModbusDevice::Error error)
302{
303 Q_D(QModbusDevice);
304
305 d->error = error;
306 d->errorString = errorText;
307 emit errorOccurred(error);
308}
309
310/*!
311 Returns the error state of the device.
312
313 \sa QModbusDevice::Error
314*/
315QModbusDevice::Error QModbusDevice::error() const
316{
317 return d_func()->error;
318}
319
320/*!
321 Returns descriptive error text for the device error.
322
323 \sa QModbusDevice::Error
324*/
325QString QModbusDevice::errorString() const
326{
327 return d_func()->errorString;
328}
329
330/*!
331 \since 5.14
332
333 Returns the underlying \l QIODevice used for ModBus communication or
334 \c nullptr if the device was not yet fully initialized.
335
336 \note Do not store a pointer to the underlying device, because it can be
337 invalidated at any point in time.
338*/
339QIODevice *QModbusDevice::device() const
340{
341 return d_func()->device();
342}
343
344/*!
345 \fn bool QModbusDevice::open()
346
347 This function is called by connectDevice(). Subclasses must provide
348 an implementation that returns \c true on successful Modbus connection
349 or connection initiation; otherwise returns \c false.
350
351 The implementation must ensure that the instance's \l state()
352 is set to \l QModbusDevice::ConnectingState or \l QModbusDevice::ConnectedState upon success; otherwise
353 \l QModbusDevice::UnconnectedState. Typically, \l QModbusDevice::ConnectingState is used
354 when the connection process reports back asynchronously and \l QModbusDevice::ConnectedState
355 in case of synchronous connect behavior.
356
357 \sa connectDevice()
358*/
359
360/*!
361 \fn void QModbusDevice::close()
362
363 This function is responsible for closing the Modbus connection.
364 The implementation must ensure that the instance's
365 \l state() is set to \l QModbusDevice::UnconnectedState.
366
367 \sa disconnectDevice()
368*/
369
370Q_LOGGING_CATEGORY(QT_MODBUS, "qt.modbus")
371Q_LOGGING_CATEGORY(QT_MODBUS_LOW, "qt.modbus.lowlevel")
372
373QT_END_NAMESPACE
374

source code of qtserialbus/src/serialbus/qmodbusdevice.cpp