1 | // Copyright (C) 2021 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 "qmodbusrtuserialclient.h" |
5 | #include "qmodbusrtuserialclient_p.h" |
6 | |
7 | #include <QtCore/qloggingcategory.h> |
8 | |
9 | QT_BEGIN_NAMESPACE |
10 | |
11 | Q_DECLARE_LOGGING_CATEGORY(QT_MODBUS) |
12 | Q_DECLARE_LOGGING_CATEGORY(QT_MODBUS_LOW) |
13 | |
14 | /*! |
15 | \class QModbusRtuSerialClient |
16 | \inmodule QtSerialBus |
17 | \since 6.2 |
18 | |
19 | \brief The QModbusRtuSerialClient class represents a Modbus client |
20 | that uses a serial bus for its communication with the Modbus server. |
21 | |
22 | Communication via Modbus requires the interaction between a single |
23 | Modbus client instance and multiple Modbus servers. This class |
24 | provides the client implementation via a serial port. |
25 | */ |
26 | |
27 | /*! |
28 | Constructs a serial Modbus client with the specified \a parent. |
29 | */ |
30 | QModbusRtuSerialClient::QModbusRtuSerialClient(QObject *parent) |
31 | : QModbusClient(*new QModbusRtuSerialClientPrivate, parent) |
32 | { |
33 | Q_D(QModbusRtuSerialClient); |
34 | d->setupSerialPort(); |
35 | } |
36 | |
37 | /*! |
38 | \internal |
39 | */ |
40 | QModbusRtuSerialClient::~QModbusRtuSerialClient() |
41 | { |
42 | close(); |
43 | } |
44 | |
45 | /*! |
46 | Returns the amount of microseconds for the silent interval between two |
47 | consecutive Modbus messages. |
48 | |
49 | \sa setInterFrameDelay() |
50 | */ |
51 | int QModbusRtuSerialClient::interFrameDelay() const |
52 | { |
53 | Q_D(const QModbusRtuSerialClient); |
54 | return d->m_interFrameDelayMilliseconds * 1000; |
55 | } |
56 | |
57 | /*! |
58 | Sets the amount of \a microseconds for the silent interval between two |
59 | consecutive Modbus messages. By default, the class implementation will use |
60 | a pre-calculated value according to the Modbus specification. A active or |
61 | running connection is not affected by such delay changes. |
62 | |
63 | \note If \a microseconds is set to -1 or \a microseconds is less than the |
64 | pre-calculated delay then this pre-calculated value is used as frame delay. |
65 | */ |
66 | void QModbusRtuSerialClient::setInterFrameDelay(int microseconds) |
67 | { |
68 | Q_D(QModbusRtuSerialClient); |
69 | d->m_interFrameDelayMilliseconds = qCeil(v: qreal(microseconds) / 1000.); |
70 | d->calculateInterFrameDelay(); |
71 | } |
72 | |
73 | /*! |
74 | \since 5.13 |
75 | |
76 | Returns the amount of milliseconds for the silent interval between a Modbus |
77 | broadcast and a consecutive Modbus messages. The default value is set to |
78 | \c 100 milliseconds. |
79 | */ |
80 | int QModbusRtuSerialClient::turnaroundDelay() const |
81 | { |
82 | Q_D(const QModbusRtuSerialClient); |
83 | return d->m_turnaroundDelay; |
84 | } |
85 | |
86 | /*! |
87 | \since 5.13 |
88 | |
89 | Sets the amount of milliseconds for the silent interval between a Modbus |
90 | broadcast and a consecutive Modbus messages to \a turnaroundDelay. |
91 | Typically the turnaround delay is in the range of \c 100 to \c 200 |
92 | milliseconds. |
93 | */ |
94 | void QModbusRtuSerialClient::setTurnaroundDelay(int turnaroundDelay) |
95 | { |
96 | Q_D(QModbusRtuSerialClient); |
97 | d->m_turnaroundDelay = turnaroundDelay; |
98 | } |
99 | |
100 | /*! |
101 | \internal |
102 | */ |
103 | QModbusRtuSerialClient::QModbusRtuSerialClient(QModbusRtuSerialClientPrivate &dd, QObject *parent) |
104 | : QModbusClient(dd, parent) |
105 | { |
106 | Q_D(QModbusRtuSerialClient); |
107 | d->setupSerialPort(); |
108 | } |
109 | |
110 | /*! |
111 | \reimp |
112 | |
113 | \note When calling this function, existing buffered data is removed from |
114 | the serial port. |
115 | */ |
116 | bool QModbusRtuSerialClient::open() |
117 | { |
118 | if (state() == QModbusDevice::ConnectedState) |
119 | return true; |
120 | |
121 | Q_D(QModbusRtuSerialClient); |
122 | d->setupEnvironment(); // to be done before open |
123 | if (d->m_serialPort->open(mode: QIODevice::ReadWrite)) { |
124 | setState(QModbusDevice::ConnectedState); |
125 | d->m_serialPort->clear(); // only possible after open |
126 | } else { |
127 | setError(errorText: d->m_serialPort->errorString(), error: QModbusDevice::ConnectionError); |
128 | } |
129 | return (state() == QModbusDevice::ConnectedState); |
130 | } |
131 | |
132 | /*! |
133 | \reimp |
134 | */ |
135 | void QModbusRtuSerialClient::close() |
136 | { |
137 | if (state() == QModbusDevice::UnconnectedState) |
138 | return; |
139 | |
140 | setState(QModbusDevice::ClosingState); |
141 | |
142 | Q_D(QModbusRtuSerialClient); |
143 | |
144 | if (d->m_serialPort->isOpen()) |
145 | d->m_serialPort->close(); |
146 | |
147 | int numberOfAborts = 0; |
148 | while (!d->m_queue.isEmpty()) { |
149 | // Finish each open reply and forget them |
150 | QModbusRtuSerialClientPrivate::QueueElement elem = d->m_queue.dequeue(); |
151 | if (!elem.reply.isNull()) { |
152 | elem.reply->setError(error: QModbusDevice::ReplyAbortedError, |
153 | errorText: QModbusClient::tr(s: "Reply aborted due to connection closure.")); |
154 | numberOfAborts++; |
155 | } |
156 | } |
157 | |
158 | if (numberOfAborts > 0) |
159 | qCDebug(QT_MODBUS_LOW) << "(RTU client) Aborted replies:"<< numberOfAborts; |
160 | |
161 | setState(QModbusDevice::UnconnectedState); |
162 | } |
163 | |
164 | QT_END_NAMESPACE |
165 | |
166 | #include "moc_qmodbusrtuserialclient.cpp" |
167 |