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