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 | |
10 | QT_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 | */ |
24 | QModbusDevice::QModbusDevice(QObject *parent) |
25 | : QObject(*new QModbusDevicePrivate, parent) |
26 | { |
27 | } |
28 | |
29 | /*! |
30 | \internal |
31 | */ |
32 | QModbusDevice::QModbusDevice(QModbusDevicePrivate &dd, QObject *parent) |
33 | : QObject(dd, parent) |
34 | { |
35 | } |
36 | |
37 | /*! |
38 | Destroys the QModbusDevice instance |
39 | */ |
40 | QModbusDevice::~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 | */ |
85 | QVariant 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 | */ |
119 | void 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 | */ |
235 | bool 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 | */ |
258 | void 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 | */ |
273 | void 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 | */ |
289 | QModbusDevice::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 | */ |
301 | void 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 | */ |
315 | QModbusDevice::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 | */ |
325 | QString 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 | */ |
339 | QIODevice *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 | |
370 | Q_LOGGING_CATEGORY(QT_MODBUS, "qt.modbus" ) |
371 | Q_LOGGING_CATEGORY(QT_MODBUS_LOW, "qt.modbus.lowlevel" ) |
372 | |
373 | QT_END_NAMESPACE |
374 | |