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
9QT_BEGIN_NAMESPACE
10
11Q_DECLARE_LOGGING_CATEGORY(QT_MODBUS)
12Q_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*/
30QModbusRtuSerialClient::QModbusRtuSerialClient(QObject *parent)
31 : QModbusClient(*new QModbusRtuSerialClientPrivate, parent)
32{
33 Q_D(QModbusRtuSerialClient);
34 d->setupSerialPort();
35}
36
37/*!
38 \internal
39*/
40QModbusRtuSerialClient::~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*/
51int 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*/
66void 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*/
80int 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*/
94void QModbusRtuSerialClient::setTurnaroundDelay(int turnaroundDelay)
95{
96 Q_D(QModbusRtuSerialClient);
97 d->m_turnaroundDelay = turnaroundDelay;
98}
99
100/*!
101 \internal
102*/
103QModbusRtuSerialClient::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*/
116bool 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*/
135void 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
164QT_END_NAMESPACE
165
166#include "moc_qmodbusrtuserialclient.cpp"
167

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