summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLiang Qi <liang.qi@qt.io>2019-04-16 09:51:50 +0200
committerLiang Qi <liang.qi@qt.io>2019-04-16 09:51:50 +0200
commit8e43d9ef9939144eddd4eb4c8442f05a827f3344 (patch)
tree7e0c748176a4f87405f6f560ec0f836d489eff09
parent960861ac561f4bd4d8ce4fbe6d0643482d961f11 (diff)
parentfe80eef6b3377c8727c5190d275808c343ee7c75 (diff)
Merge remote-tracking branch 'origin/5.13' into dev
Conflicts: examples/serialbus/modbus/adueditor/modbustcpclient_p.h Change-Id: I620bfce752a957fdcd068c44ff039d84b7502edc
-rw-r--r--examples/serialbus/modbus/adueditor/modbustcpclient_p.h2
-rw-r--r--src/plugins/canbus/systeccan/systeccanbackend.cpp6
-rw-r--r--src/serialbus/qmodbusclient.cpp5
-rw-r--r--src/serialbus/qmodbusclient_p.h1
-rw-r--r--src/serialbus/qmodbusreply.cpp9
-rw-r--r--src/serialbus/qmodbusreply.h3
-rw-r--r--src/serialbus/qmodbusrtuserialmaster.cpp31
-rw-r--r--src/serialbus/qmodbusrtuserialmaster.h3
-rw-r--r--src/serialbus/qmodbusrtuserialmaster_p.h500
-rw-r--r--src/serialbus/qmodbusrtuserialslave_p.h6
-rw-r--r--src/serialbus/qmodbustcpclient_p.h10
-rw-r--r--src/serialbus/qmodbustcpserver_p.h15
12 files changed, 331 insertions, 260 deletions
diff --git a/examples/serialbus/modbus/adueditor/modbustcpclient_p.h b/examples/serialbus/modbus/adueditor/modbustcpclient_p.h
index a563b6f..c4a1c13 100644
--- a/examples/serialbus/modbus/adueditor/modbustcpclient_p.h
+++ b/examples/serialbus/modbus/adueditor/modbustcpclient_p.h
@@ -97,7 +97,7 @@ public:
q->connect(q, &QModbusClient::timeoutChanged,
element.timer.data(), QOverload<int>::of(&QTimer::setInterval));
- q->connect(element.timer.data(), &QTimer::timeout, [this, writeToSocket]() {
+ QObject::connect(element.timer.data(), &QTimer::timeout, q, [this, writeToSocket]() {
if (!m_transactionStore.contains(m_tId))
return;
diff --git a/src/plugins/canbus/systeccan/systeccanbackend.cpp b/src/plugins/canbus/systeccan/systeccanbackend.cpp
index f33d9ba..3fdd135 100644
--- a/src/plugins/canbus/systeccan/systeccanbackend.cpp
+++ b/src/plugins/canbus/systeccan/systeccanbackend.cpp
@@ -479,11 +479,11 @@ bool SystecCanBackend::open()
if (!d->open())
return false;
- // Apply all stored configurations except bitrate, because
- // the bitrate can not be applied after opening the device
+ // Apply all stored configurations except bitrate and receive own,
+ // because these cannot be applied after opening the device
const QVector<int> keys = configurationKeys();
for (int key : keys) {
- if (key == QCanBusDevice::BitRateKey)
+ if (key == BitRateKey || key == ReceiveOwnKey)
continue;
const QVariant param = configurationParameter(key);
const bool success = d->setConfigurationParameter(key, param);
diff --git a/src/serialbus/qmodbusclient.cpp b/src/serialbus/qmodbusclient.cpp
index 32be316..d273a3a 100644
--- a/src/serialbus/qmodbusclient.cpp
+++ b/src/serialbus/qmodbusclient.cpp
@@ -351,6 +351,9 @@ QModbusRequest QModbusClientPrivate::createRWRequest(const QModbusDataUnit &read
void QModbusClientPrivate::processQueueElement(const QModbusResponse &pdu,
const QueueElement &element)
{
+ if (element.reply.isNull())
+ return;
+
element.reply->setRawResult(pdu);
if (pdu.isException()) {
element.reply->setError(QModbusDevice::ProtocolError,
@@ -358,7 +361,7 @@ void QModbusClientPrivate::processQueueElement(const QModbusResponse &pdu,
return;
}
- if (element.reply->type() == QModbusReply::Raw) {
+ if (element.reply->type() != QModbusReply::Common) {
element.reply->setFinished(true);
return;
}
diff --git a/src/serialbus/qmodbusclient_p.h b/src/serialbus/qmodbusclient_p.h
index 2286fc8..f9a0dfb 100644
--- a/src/serialbus/qmodbusclient_p.h
+++ b/src/serialbus/qmodbusclient_p.h
@@ -126,6 +126,7 @@ public:
QSharedPointer<QTimer> timer;
QByteArray adu;
qint64 bytesWritten = 0;
+ qint32 m_timerId = INT_MIN;
};
void processQueueElement(const QModbusResponse &pdu, const QueueElement &element);
};
diff --git a/src/serialbus/qmodbusreply.cpp b/src/serialbus/qmodbusreply.cpp
index c8b328f..7201a33 100644
--- a/src/serialbus/qmodbusreply.cpp
+++ b/src/serialbus/qmodbusreply.cpp
@@ -74,6 +74,9 @@ public:
\value Common The reply originates from a common read, write or read/write
request. See \l QModbusClient::sendReadRequest,
\l QModbusClient::sendWriteRequest and \l QModbusClient::sendReadWriteRequest
+ \value Broadcast The replay originates from a Modbus broadcast request. The
+ \l serverAddress() will return \c 0 and the \l finished()
+ signal will be emitted immediately.
*/
/*!
@@ -92,6 +95,8 @@ QModbusReply::QModbusReply(ReplyType type, int serverAddress, QObject *parent)
/*!
Returns \c true when the reply has finished or was aborted.
+
+ \sa finished(), error()
*/
bool QModbusReply::isFinished() const
{
@@ -187,10 +192,14 @@ int QModbusReply::serverAddress() const
Note: Do not delete this reply object in the slot connected to this signal.
Use \l deleteLater() instead.
+
+ \sa error(), errorString()
*/
/*!
Returns the error state of this reply.
+
+ \sa errorString(), errorOccurred()
*/
QModbusDevice::Error QModbusReply::error() const
{
diff --git a/src/serialbus/qmodbusreply.h b/src/serialbus/qmodbusreply.h
index 9b54687..ffefc89 100644
--- a/src/serialbus/qmodbusreply.h
+++ b/src/serialbus/qmodbusreply.h
@@ -53,7 +53,8 @@ class Q_SERIALBUS_EXPORT QModbusReply : public QObject
public:
enum ReplyType {
Raw,
- Common
+ Common,
+ Broadcast
};
Q_ENUM(ReplyType)
diff --git a/src/serialbus/qmodbusrtuserialmaster.cpp b/src/serialbus/qmodbusrtuserialmaster.cpp
index 99006f4..a54b253 100644
--- a/src/serialbus/qmodbusrtuserialmaster.cpp
+++ b/src/serialbus/qmodbusrtuserialmaster.cpp
@@ -104,6 +104,33 @@ void QModbusRtuSerialMaster::setInterFrameDelay(int microseconds)
}
/*!
+ \since 5.13
+
+ Returns the amount of milliseconds for the silent interval between a Modbus
+ broadcast and a consecutive Modbus messages. The default value is set to
+ \c 100 milliseconds.
+*/
+int QModbusRtuSerialMaster::turnaroundDelay() const
+{
+ Q_D(const QModbusRtuSerialMaster);
+ return d->m_turnaroundDelay;
+}
+
+/*!
+ \since 5.13
+
+ Sets the amount of milliseconds for the silent interval between a Modbus
+ broadcast and a consecutive Modbus messages to \a turnaroundDelay.
+ Typically the turnaround delay is in the range of \c 100 to \c 200
+ milliseconds.
+*/
+void QModbusRtuSerialMaster::setTurnaroundDelay(int turnaroundDelay)
+{
+ Q_D(QModbusRtuSerialMaster);
+ d->m_turnaroundDelay = turnaroundDelay;
+}
+
+/*!
\internal
*/
QModbusRtuSerialMaster::QModbusRtuSerialMaster(QModbusRtuSerialMasterPrivate &dd, QObject *parent)
@@ -150,10 +177,6 @@ void QModbusRtuSerialMaster::close()
if (d->m_serialPort->isOpen())
d->m_serialPort->close();
- // enqueue current active request back for abortion
- d->m_queue.enqueue(d->m_current);
- d->m_current = QModbusClientPrivate::QueueElement();
-
int numberOfAborts = 0;
while (!d->m_queue.isEmpty()) {
// Finish each open reply and forget them
diff --git a/src/serialbus/qmodbusrtuserialmaster.h b/src/serialbus/qmodbusrtuserialmaster.h
index 5c607e2..87f58df 100644
--- a/src/serialbus/qmodbusrtuserialmaster.h
+++ b/src/serialbus/qmodbusrtuserialmaster.h
@@ -55,6 +55,9 @@ public:
int interFrameDelay() const;
void setInterFrameDelay(int microseconds);
+ int turnaroundDelay() const;
+ void setTurnaroundDelay(int turnaroundDelay);
+
protected:
QModbusRtuSerialMaster(QModbusRtuSerialMasterPrivate &dd, QObject *parent = nullptr);
diff --git a/src/serialbus/qmodbusrtuserialmaster_p.h b/src/serialbus/qmodbusrtuserialmaster_p.h
index 5e440b2..9672684 100644
--- a/src/serialbus/qmodbusrtuserialmaster_p.h
+++ b/src/serialbus/qmodbusrtuserialmaster_p.h
@@ -65,174 +65,254 @@ QT_BEGIN_NAMESPACE
Q_DECLARE_LOGGING_CATEGORY(QT_MODBUS)
Q_DECLARE_LOGGING_CATEGORY(QT_MODBUS_LOW)
+class Timer : public QObject
+{
+ Q_OBJECT
+
+public:
+ Timer() = default;
+ int start(int msec)
+ {
+ m_timer = QBasicTimer();
+ m_timer.start(msec, Qt::PreciseTimer, this);
+ return m_timer.timerId();
+ }
+ void stop() { m_timer.stop(); }
+ bool isActive() const { return m_timer.isActive(); }
+
+signals:
+ void timeout(int timerId);
+
+private:
+ void timerEvent(QTimerEvent *event) override
+ {
+ const auto id = m_timer.timerId();
+ if (event->timerId() == id)
+ emit timeout(id);
+ }
+
+private:
+ QBasicTimer m_timer;
+};
+
class QModbusRtuSerialMasterPrivate : public QModbusClientPrivate
{
Q_DECLARE_PUBLIC(QModbusRtuSerialMaster)
- enum State {
+ enum State
+ {
Idle,
- Schedule,
- Send,
- Receive,
+ WaitingForReplay,
+ ProcessReply
} m_state = Idle;
public:
- void setupSerialPort()
+ void onReadyRead()
{
- Q_Q(QModbusRtuSerialMaster);
+ m_responseBuffer += m_serialPort->read(m_serialPort->bytesAvailable());
+ qCDebug(QT_MODBUS_LOW) << "(RTU client) Response buffer:" << m_responseBuffer.toHex();
- m_sendTimer.setSingleShot(true);
- QObject::connect(&m_sendTimer, &QTimer::timeout, q, [this]() { processQueue(); });
+ if (m_responseBuffer.size() < 2) {
+ qCDebug(QT_MODBUS) << "(RTU client) Modbus ADU not complete";
+ return;
+ }
- m_responseTimer.setSingleShot(true);
- QObject::connect(&m_responseTimer, &QTimer::timeout, q, [this]() { processQueue(); });
+ const QModbusSerialAdu tmpAdu(QModbusSerialAdu::Rtu, m_responseBuffer);
+ int pduSizeWithoutFcode = QModbusResponse::calculateDataSize(tmpAdu.pdu());
+ if (pduSizeWithoutFcode < 0) {
+ // wait for more data
+ qCDebug(QT_MODBUS) << "(RTU client) Cannot calculate PDU size for function code:"
+ << tmpAdu.pdu().functionCode() << ", delaying pending frame";
+ return;
+ }
- m_serialPort = new QSerialPort(q);
- QObject::connect(m_serialPort, &QSerialPort::readyRead, q, [this]() {
- responseBuffer += m_serialPort->read(m_serialPort->bytesAvailable());
- qCDebug(QT_MODBUS_LOW) << "(RTU client) Response buffer:" << responseBuffer.toHex();
+ // server address byte + function code byte + PDU size + 2 bytes CRC
+ int aduSize = 2 + pduSizeWithoutFcode + 2;
+ if (tmpAdu.rawSize() < aduSize) {
+ qCDebug(QT_MODBUS) << "(RTU client) Incomplete ADU received, ignoring";
+ return;
+ }
- if (responseBuffer.size() < 2) {
- qCDebug(QT_MODBUS) << "(RTU client) Modbus ADU not complete";
- return;
+ if (m_queue.isEmpty())
+ return;
+ auto &current = m_queue.first();
+
+ // Special case for Diagnostics:ReturnQueryData. The response has no
+ // length indicator and is just a simple echo of what we have send.
+ if (tmpAdu.pdu().functionCode() == QModbusPdu::Diagnostics) {
+ const QModbusResponse response = tmpAdu.pdu();
+ if (canMatchRequestAndResponse(response, tmpAdu.serverAddress())) {
+ quint16 subCode = 0xffff;
+ response.decodeData(&subCode);
+ if (subCode == Diagnostics::ReturnQueryData) {
+ if (response.data() != current.requestPdu.data())
+ return; // echo does not match request yet
+ aduSize = 2 + response.dataSize() + 2;
+ if (tmpAdu.rawSize() < aduSize)
+ return; // echo matches, probably checksum missing
+ }
}
+ }
- const QModbusSerialAdu tmpAdu(QModbusSerialAdu::Rtu, responseBuffer);
- int pduSizeWithoutFcode = QModbusResponse::calculateDataSize(tmpAdu.pdu());
- if (pduSizeWithoutFcode < 0) {
- // wait for more data
- qCDebug(QT_MODBUS) << "(RTU client) Cannot calculate PDU size for function code:"
- << tmpAdu.pdu().functionCode() << ", delaying pending frame";
- return;
- }
+ const QModbusSerialAdu adu(QModbusSerialAdu::Rtu, m_responseBuffer.left(aduSize));
+ m_responseBuffer.remove(0, aduSize);
- // server address byte + function code byte + PDU size + 2 bytes CRC
- int aduSize = 2 + pduSizeWithoutFcode + 2;
- if (tmpAdu.rawSize() < aduSize) {
- qCDebug(QT_MODBUS) << "(RTU client) Incomplete ADU received, ignoring";
- return;
- }
+ qCDebug(QT_MODBUS) << "(RTU client) Received ADU:" << adu.rawData().toHex();
+ if (QT_MODBUS().isDebugEnabled() && !m_responseBuffer.isEmpty())
+ qCDebug(QT_MODBUS_LOW) << "(RTU client) Pending buffer:" << m_responseBuffer.toHex();
- // Special case for Diagnostics:ReturnQueryData. The response has no
- // length indicator and is just a simple echo of what we have send.
- if (tmpAdu.pdu().functionCode() == QModbusPdu::Diagnostics) {
- const QModbusResponse response = tmpAdu.pdu();
- if (canMatchRequestAndResponse(response, tmpAdu.serverAddress())) {
- quint16 subCode = 0xffff;
- response.decodeData(&subCode);
- if (subCode == Diagnostics::ReturnQueryData) {
- if (response.data() != m_current.requestPdu.data())
- return; // echo does not match request yet
- aduSize = 2 + response.dataSize() + 2;
- if (tmpAdu.rawSize() < aduSize)
- return; // echo matches, probably checksum missing
- }
- }
- }
+ // check CRC
+ if (!adu.matchingChecksum()) {
+ qCWarning(QT_MODBUS) << "(RTU client) Discarding response with wrong CRC, received:"
+ << adu.checksum<quint16>() << ", calculated CRC:"
+ << QModbusSerialAdu::calculateCRC(adu.data(), adu.size());
+ return;
+ }
- const QModbusSerialAdu adu(QModbusSerialAdu::Rtu, responseBuffer.left(aduSize));
- responseBuffer.remove(0, aduSize);
+ const QModbusResponse response = adu.pdu();
+ if (!canMatchRequestAndResponse(response, adu.serverAddress())) {
+ qCWarning(QT_MODBUS) << "(RTU client) Cannot match response with open request, "
+ "ignoring";
+ return;
+ }
- qCDebug(QT_MODBUS)<< "(RTU client) Received ADU:" << adu.rawData().toHex();
- if (QT_MODBUS().isDebugEnabled() && !responseBuffer.isEmpty())
- qCDebug(QT_MODBUS_LOW) << "(RTU client) Pending buffer:" << responseBuffer.toHex();
+ m_state = ProcessReply;
+ m_responseTimer.stop();
+ current.m_timerId = INT_MIN;
- // check CRC
- if (!adu.matchingChecksum()) {
- qCWarning(QT_MODBUS) << "(RTU client) Discarding response with wrong CRC, received:"
- << adu.checksum<quint16>() << ", calculated CRC:"
- << QModbusSerialAdu::calculateCRC(adu.data(), adu.size());
- return;
- }
+ processQueueElement(response, m_queue.dequeue());
- const QModbusResponse response = adu.pdu();
- if (!canMatchRequestAndResponse(response, adu.serverAddress())) {
- qCWarning(QT_MODBUS) << "(RTU client) Cannot match response with open request, "
- "ignoring";
- return;
- }
+ m_state = Idle;
+ scheduleNextRequest(m_interFrameDelayMilliseconds);
+ }
- if (m_state != State::Receive) {
- qCDebug(QT_MODBUS) << "(RTU server) Ignoring response due to non receive state";
- return;
- }
+ void onAboutToClose()
+ {
+ Q_Q(QModbusRtuSerialMaster);
+ Q_UNUSED(q) // avoid warning in release mode
+ Q_ASSERT(q->state() == QModbusDevice::ClosingState);
+
+ m_responseTimer.stop();
+ }
+
+ void onResponseTimeout(int timerId)
+ {
+ m_responseTimer.stop();
+ if (m_state != State::WaitingForReplay || m_queue.isEmpty())
+ return;
+ const auto current = m_queue.first();
- m_sendTimer.stop();
- m_responseTimer.stop();
- processQueueElement(response, m_current);
+ if (current.m_timerId != timerId)
+ return;
- m_state = Schedule; // reschedule, even if empty
- m_serialPort->clear(QSerialPort::AllDirections);
- QTimer::singleShot(m_interFrameDelayMilliseconds, [this]() { processQueue(); });
- });
+ qCDebug(QT_MODBUS) << "(RTU client) Receive timeout:" << current.requestPdu;
- using TypeId = void (QSerialPort::*)(QSerialPort::SerialPortError);
- QObject::connect(m_serialPort, static_cast<TypeId>(&QSerialPort::error),
- [this](QSerialPort::SerialPortError error) {
- if (error == QSerialPort::NoError)
- return;
-
- qCDebug(QT_MODBUS) << "(RTU server) QSerialPort error:" << error
- << (m_serialPort ? m_serialPort->errorString() : QString());
-
- Q_Q(QModbusRtuSerialMaster);
-
- switch (error) {
- case QSerialPort::DeviceNotFoundError:
- q->setError(QModbusDevice::tr("Referenced serial device does not exist."),
- QModbusDevice::ConnectionError);
- break;
- case QSerialPort::PermissionError:
- q->setError(QModbusDevice::tr("Cannot open serial device due to permissions."),
- QModbusDevice::ConnectionError);
- break;
- case QSerialPort::OpenError:
- case QSerialPort::NotOpenError:
- q->setError(QModbusDevice::tr("Cannot open serial device."),
- QModbusDevice::ConnectionError);
- break;
- case QSerialPort::WriteError:
- q->setError(QModbusDevice::tr("Write error."), QModbusDevice::WriteError);
- break;
- case QSerialPort::ReadError:
- q->setError(QModbusDevice::tr("Read error."), QModbusDevice::ReadError);
- break;
- case QSerialPort::ResourceError:
- q->setError(QModbusDevice::tr("Resource error."), QModbusDevice::ConnectionError);
- break;
- case QSerialPort::UnsupportedOperationError:
- q->setError(QModbusDevice::tr("Device operation is not supported error."),
- QModbusDevice::ConfigurationError);
- break;
- case QSerialPort::TimeoutError:
- q->setError(QModbusDevice::tr("Timeout error."), QModbusDevice::TimeoutError);
- break;
- case QSerialPort::UnknownError:
- q->setError(QModbusDevice::tr("Unknown error."), QModbusDevice::UnknownError);
- break;
- default:
- qCDebug(QT_MODBUS) << "(RTU server) Unhandled QSerialPort error" << error;
- break;
+ if (current.numberOfRetries <= 0) {
+ auto item = m_queue.dequeue();
+ if (item.reply) {
+ item.reply->setError(QModbusDevice::TimeoutError,
+ QModbusClient::tr("Request timeout."));
}
+ }
+
+ m_state = Idle;
+ scheduleNextRequest(m_interFrameDelayMilliseconds);
+ }
+
+ void onBytesWritten(qint64 bytes)
+ {
+ if (m_queue.isEmpty())
+ return;
+ auto &current = m_queue.first();
+
+ current.bytesWritten += bytes;
+ if (current.bytesWritten != current.adu.size())
+ return;
+
+ qCDebug(QT_MODBUS) << "(RTU client) Send successful:" << current.requestPdu;
+
+ if (!current.reply.isNull() && current.reply->type() == QModbusReply::Broadcast) {
+ m_state = ProcessReply;
+ processQueueElement({}, m_queue.dequeue());
+ m_state = Idle;
+ scheduleNextRequest(m_turnaroundDelay);
+ } else {
+ current.m_timerId = m_responseTimer.start(m_responseTimeoutDuration);
+ }
+ }
+
+ void onError(QSerialPort::SerialPortError error)
+ {
+ if (error == QSerialPort::NoError)
+ return;
+
+ qCDebug(QT_MODBUS) << "(RTU server) QSerialPort error:" << error
+ << (m_serialPort ? m_serialPort->errorString() : QString());
+
+ Q_Q(QModbusRtuSerialMaster);
+
+ switch (error) {
+ case QSerialPort::DeviceNotFoundError:
+ q->setError(QModbusDevice::tr("Referenced serial device does not exist."),
+ QModbusDevice::ConnectionError);
+ break;
+ case QSerialPort::PermissionError:
+ q->setError(QModbusDevice::tr("Cannot open serial device due to permissions."),
+ QModbusDevice::ConnectionError);
+ break;
+ case QSerialPort::OpenError:
+ case QSerialPort::NotOpenError:
+ q->setError(QModbusDevice::tr("Cannot open serial device."),
+ QModbusDevice::ConnectionError);
+ break;
+ case QSerialPort::WriteError:
+ q->setError(QModbusDevice::tr("Write error."), QModbusDevice::WriteError);
+ break;
+ case QSerialPort::ReadError:
+ q->setError(QModbusDevice::tr("Read error."), QModbusDevice::ReadError);
+ break;
+ case QSerialPort::ResourceError:
+ q->setError(QModbusDevice::tr("Resource error."), QModbusDevice::ConnectionError);
+ break;
+ case QSerialPort::UnsupportedOperationError:
+ q->setError(QModbusDevice::tr("Device operation is not supported error."),
+ QModbusDevice::ConfigurationError);
+ break;
+ case QSerialPort::TimeoutError:
+ q->setError(QModbusDevice::tr("Timeout error."), QModbusDevice::TimeoutError);
+ break;
+ case QSerialPort::UnknownError:
+ q->setError(QModbusDevice::tr("Unknown error."), QModbusDevice::UnknownError);
+ break;
+ default:
+ qCDebug(QT_MODBUS) << "(RTU server) Unhandled QSerialPort error" << error;
+ break;
+ }
+ }
+
+ void setupSerialPort()
+ {
+ Q_Q(QModbusRtuSerialMaster);
+ m_serialPort = new QSerialPort(q);
+
+ QObject::connect(&m_responseTimer, &Timer::timeout, q, [this](int timerId) {
+ onResponseTimeout(timerId);
});
- QObject::connect(m_serialPort, &QSerialPort::bytesWritten, q, [this](qint64 bytes) {
- m_current.bytesWritten += bytes;
- if (m_state == Send && (m_current.bytesWritten == m_current.adu.size()) && !m_current.reply.isNull()) {
- // the if conditions above are copied from processQueue()
- qCDebug(QT_MODBUS) << "(RTU client) Send successful (quick):" << m_current.requestPdu;
- m_state = Receive;
- m_sendTimer.stop();
- m_responseTimer.start(m_responseTimeoutDuration);
- }
+ QObject::connect(m_serialPort, &QSerialPort::readyRead, q, [this]() {
+ onReadyRead();
});
QObject::connect(m_serialPort, &QSerialPort::aboutToClose, q, [this]() {
- Q_Q(QModbusRtuSerialMaster);
- Q_UNUSED(q); // To avoid unused variable warning in release mode
- Q_ASSERT(q->state() == QModbusDevice::ClosingState);
+ onAboutToClose();
+ });
- m_sendTimer.stop();
- m_responseTimer.stop();
+ QObject::connect(m_serialPort, &QSerialPort::bytesWritten, q, [this](qint64 bytes) {
+ onBytesWritten(bytes);
+ });
+
+ QObject::connect(m_serialPort, QOverload<QSerialPort::SerialPortError>::of(&QSerialPort::error),
+ q, [this](QSerialPort::SerialPortError error) {
+ onError(error);
});
}
@@ -244,7 +324,8 @@ public:
If the user set the timeout to be longer than the calculated one,
we'll keep the user defined.
*/
- void calculateInterFrameDelay() {
+ void calculateInterFrameDelay()
+ {
// The spec recommends a timeout value of 1.750 msec. Without such
// precise single-shot timers use a approximated value of 1.750 msec.
int delayMilliSeconds = 2;
@@ -258,7 +339,8 @@ public:
m_interFrameDelayMilliseconds = delayMilliSeconds;
}
- void setupEnvironment() {
+ void setupEnvironment()
+ {
if (m_serialPort) {
m_serialPort->setPortName(m_comPort);
m_serialPort->setParity(m_parity);
@@ -269,123 +351,71 @@ public:
calculateInterFrameDelay();
- responseBuffer.clear();
+ m_responseBuffer.clear();
m_state = QModbusRtuSerialMasterPrivate::Idle;
}
- void scheduleNextRequest() {
- Q_Q(QModbusRtuSerialMaster);
-
- m_state = Schedule;
- m_serialPort->clear(QSerialPort::AllDirections);
- QTimer::singleShot(m_interFrameDelayMilliseconds, q, [this]() { processQueue(); });
- }
-
QModbusReply *enqueueRequest(const QModbusRequest &request, int serverAddress,
const QModbusDataUnit &unit, QModbusReply::ReplyType type) override
{
Q_Q(QModbusRtuSerialMaster);
- auto reply = new QModbusReply(type, serverAddress, q);
+ auto reply = new QModbusReply(serverAddress == 0 ? QModbusReply::Broadcast : type,
+ serverAddress, q);
QueueElement element(reply, request, unit, m_numberOfRetries + 1);
element.adu = QModbusSerialAdu::create(QModbusSerialAdu::Rtu, serverAddress, request);
m_queue.enqueue(element);
- if (m_state == Idle)
- scheduleNextRequest();
+ scheduleNextRequest(m_interFrameDelayMilliseconds);
+
return reply;
}
- void processQueue()
+ void scheduleNextRequest(int delay)
{
- Q_ASSERT_X(!m_sendTimer.isActive(), "processQueue", "send timer active");
- Q_ASSERT_X(!m_responseTimer.isActive(), "processQueue", "response timer active");
-
- auto writeAdu = [this]() {
- responseBuffer.clear();
- m_current.bytesWritten = 0;
- m_current.numberOfRetries--;
- m_serialPort->write(m_current.adu);
- m_sendTimer.start(m_interFrameDelayMilliseconds);
-
- qCDebug(QT_MODBUS) << "(RTU client) Sent Serial PDU:" << m_current.requestPdu;
- qCDebug(QT_MODBUS_LOW).noquote() << "(RTU client) Sent Serial ADU: 0x" + m_current.adu
- .toHex();
- };
-
- switch (m_state) {
- case Schedule:
- m_current = QueueElement();
- if (!m_queue.isEmpty()) {
- m_current = m_queue.dequeue();
- if (m_current.reply) {
- m_state = Send;
- QTimer::singleShot(0, [writeAdu]() { writeAdu(); });
- } else {
- QTimer::singleShot(0, [this]() { processQueue(); });
- }
- } else {
- m_state = Idle;
- }
- break;
+ Q_Q(QModbusRtuSerialMaster);
- case Send:
- // send timeout will always happen unless canceled by very quick bytesWritten
- if (m_current.reply.isNull()) {
- scheduleNextRequest();
- } else if (m_current.bytesWritten < m_current.adu.size()) {
- qCDebug(QT_MODBUS) << "(RTU client) Send failed:" << m_current.requestPdu;
-
- if (m_current.numberOfRetries <= 0) {
- if (m_current.reply) {
- m_current.reply->setError(QModbusDevice::TimeoutError,
- QModbusClient::tr("Request timeout."));
- }
- m_current = QueueElement();
- scheduleNextRequest();
- } else {
- m_serialPort->clear(QSerialPort::AllDirections);
- QTimer::singleShot(m_interFrameDelayMilliseconds, [writeAdu]() { writeAdu(); });
- }
- } else {
- qCDebug(QT_MODBUS) << "(RTU client) Send successful:" << m_current.requestPdu;
- m_state = Receive;
- m_responseTimer.start(m_responseTimeoutDuration);
- }
- break;
+ if (m_state == Idle && !m_queue.isEmpty()) {
+ m_state = WaitingForReplay;
+ QTimer::singleShot(delay, q, [this]() { processQueue(); });
+ }
+ }
- case Receive:
- // receive timeout will only happen after successful send
- qCDebug(QT_MODBUS) << "(RTU client) Receive timeout:" << m_current.requestPdu;
- if (m_current.reply.isNull()) {
- scheduleNextRequest();
- } else if (m_current.numberOfRetries <= 0) {
- if (m_current.reply) {
- m_current.reply->setError(QModbusDevice::TimeoutError,
- QModbusClient::tr("Response timeout."));
- }
- scheduleNextRequest();
- } else {
- m_state = Send;
- m_serialPort->clear(QSerialPort::AllDirections);
- QTimer::singleShot(m_interFrameDelayMilliseconds, [writeAdu]() { writeAdu(); });
- }
- break;
+ void processQueue()
+ {
+ m_responseBuffer.clear();
+ m_serialPort->clear(QSerialPort::AllDirections);
- case Idle:
- default:
- Q_ASSERT_X(false, "processQueue", QByteArray("unexpected state: ").append(m_state));
- break;
+ if (m_queue.isEmpty())
+ return;
+ auto &current = m_queue.first();
+
+ if (current.reply.isNull()) {
+ m_queue.dequeue();
+ m_state = Idle;
+ scheduleNextRequest(m_interFrameDelayMilliseconds);
+ } else {
+ current.bytesWritten = 0;
+ current.numberOfRetries--;
+ m_serialPort->write(current.adu);
+
+ qCDebug(QT_MODBUS) << "(RTU client) Sent Serial PDU:" << current.requestPdu;
+ qCDebug(QT_MODBUS_LOW).noquote() << "(RTU client) Sent Serial ADU: 0x" + current.adu
+ .toHex();
}
}
bool canMatchRequestAndResponse(const QModbusResponse &response, int sendingServer) const
{
- if (m_current.reply.isNull())
+ if (m_queue.isEmpty())
+ return false;
+ const auto &current = m_queue.first();
+
+ if (current.reply.isNull())
return false; // reply deleted
- if (m_current.reply->serverAddress() != sendingServer)
+ if (current.reply->serverAddress() != sendingServer)
return false; // server mismatch
- if (m_current.requestPdu.functionCode() != response.functionCode())
+ if (current.requestPdu.functionCode() != response.functionCode())
return false; // request for different function code
return true;
}
@@ -397,18 +427,18 @@ public:
return false;
}
- QTimer m_sendTimer;
- QTimer m_responseTimer;
-
- QueueElement m_current;
- QByteArray responseBuffer;
+ Timer m_responseTimer;
+ QByteArray m_responseBuffer;
QQueue<QueueElement> m_queue;
QSerialPort *m_serialPort = nullptr;
int m_interFrameDelayMilliseconds = 2; // A approximated value of 1.750 msec.
+ int m_turnaroundDelay = 100; // Recommended value is between 100 and 200 msec.
};
QT_END_NAMESPACE
+#include "qmodbusrtuserialmaster_p.h"
+
#endif // QMODBUSSERIALMASTER_P_H
diff --git a/src/serialbus/qmodbusrtuserialslave_p.h b/src/serialbus/qmodbusrtuserialslave_p.h
index 5c78d82..da59e5e 100644
--- a/src/serialbus/qmodbusrtuserialslave_p.h
+++ b/src/serialbus/qmodbusrtuserialslave_p.h
@@ -74,7 +74,7 @@ public:
Q_Q(QModbusRtuSerialSlave);
m_serialPort = new QSerialPort(q);
- QObject::connect(m_serialPort, &QSerialPort::readyRead, [this]() {
+ QObject::connect(m_serialPort, &QSerialPort::readyRead, q, [this]() {
if (m_interFrameTimer.isValid()
&& m_interFrameTimer.elapsed() > m_interFrameDelayMilliseconds
@@ -281,7 +281,7 @@ public:
});
using TypeId = void (QSerialPort::*)(QSerialPort::SerialPortError);
- QObject::connect(m_serialPort, static_cast<TypeId>(&QSerialPort::error),
+ QObject::connect(m_serialPort, static_cast<TypeId>(&QSerialPort::error), q,
[this](QSerialPort::SerialPortError error) {
if (error == QSerialPort::NoError)
return;
@@ -330,7 +330,7 @@ public:
}
});
- QObject::connect(m_serialPort, &QSerialPort::aboutToClose, [this]() {
+ QObject::connect(m_serialPort, &QSerialPort::aboutToClose, q, [this]() {
Q_Q(QModbusRtuSerialSlave);
// update state if socket closure was caused by remote side
if (q->state() != QModbusDevice::ClosingState)
diff --git a/src/serialbus/qmodbustcpclient_p.h b/src/serialbus/qmodbustcpclient_p.h
index e4dfa9e..db1d4e4 100644
--- a/src/serialbus/qmodbustcpclient_p.h
+++ b/src/serialbus/qmodbustcpclient_p.h
@@ -71,7 +71,7 @@ public:
m_socket = new QTcpSocket(q);
- QObject::connect(m_socket, &QAbstractSocket::connected, [this]() {
+ QObject::connect(m_socket, &QAbstractSocket::connected, q, [this]() {
qCDebug(QT_MODBUS) << "(TCP client) Connected to" << m_socket->peerAddress()
<< "on port" << m_socket->peerPort();
Q_Q(QModbusTcpClient);
@@ -79,7 +79,7 @@ public:
q->setState(QModbusDevice::ConnectedState);
});
- QObject::connect(m_socket, &QAbstractSocket::disconnected, [this]() {
+ QObject::connect(m_socket, &QAbstractSocket::disconnected, q, [this]() {
qCDebug(QT_MODBUS) << "(TCP client) Connection closed.";
Q_Q(QModbusTcpClient);
q->setState(QModbusDevice::UnconnectedState);
@@ -87,7 +87,7 @@ public:
});
using TypeId = void (QAbstractSocket::*)(QAbstractSocket::SocketError);
- QObject::connect(m_socket, static_cast<TypeId>(&QAbstractSocket::error),
+ QObject::connect(m_socket, static_cast<TypeId>(&QAbstractSocket::error), q,
[this](QAbstractSocket::SocketError /*error*/)
{
Q_Q(QModbusTcpClient);
@@ -100,7 +100,7 @@ public:
QModbusDevice::ConnectionError);
});
- QObject::connect(m_socket, &QIODevice::readyRead, [this](){
+ QObject::connect(m_socket, &QIODevice::readyRead, q, [this](){
responseBuffer += m_socket->read(m_socket->bytesAvailable());
qCDebug(QT_MODBUS_LOW) << "(TCP client) Response buffer:" << responseBuffer.toHex();
@@ -196,7 +196,7 @@ public:
using TypeId = void (QTimer::*)(int);
q->connect(q, &QModbusClient::timeoutChanged,
element.timer.data(), static_cast<TypeId>(&QTimer::setInterval));
- QObject::connect(element.timer.data(), &QTimer::timeout, [this, writeToSocket, tId]() {
+ QObject::connect(element.timer.data(), &QTimer::timeout, q, [this, writeToSocket, tId]() {
if (!m_transactionStore.contains(tId))
return;
diff --git a/src/serialbus/qmodbustcpserver_p.h b/src/serialbus/qmodbustcpserver_p.h
index 91e3bfe..56c45e9 100644
--- a/src/serialbus/qmodbustcpserver_p.h
+++ b/src/serialbus/qmodbustcpserver_p.h
@@ -115,9 +115,9 @@ public:
void setupTcpServer()
{
- Q_Q(QModbusTcpServer);
- m_tcpServer = new QTcpServer(q);
- QObject::connect(m_tcpServer, &QTcpServer::newConnection, [this]() {
+ m_tcpServer = new QTcpServer(q_func());
+ QObject::connect(m_tcpServer, &QTcpServer::newConnection, q_func(), [this]() {
+ Q_Q(QModbusTcpServer);
auto *socket = m_tcpServer->nextPendingConnection();
if (!socket)
return;
@@ -136,18 +136,18 @@ public:
auto buffer = new QByteArray();
- QObject::connect(socket, &QObject::destroyed, [buffer]() {
+ QObject::connect(socket, &QObject::destroyed, q, [buffer]() {
// cleanup buffer
delete buffer;
});
- QObject::connect(socket, &QTcpSocket::disconnected, [socket, this]() {
+ QObject::connect(socket, &QTcpSocket::disconnected, q, [socket, this]() {
connections.removeAll(socket);
Q_Q(QModbusTcpServer);
emit q->modbusClientDisconnected(socket);
socket->deleteLater();
});
- QObject::connect(socket, &QTcpSocket::readyRead, [buffer, socket, this]() {
+ QObject::connect(socket, &QTcpSocket::readyRead, q, [buffer, socket, this]() {
if (!socket)
return;
@@ -214,7 +214,8 @@ public:
}
});
});
- QObject::connect(m_tcpServer, &QTcpServer::acceptError,
+
+ QObject::connect(m_tcpServer, &QTcpServer::acceptError, q_func(),
[this](QAbstractSocket::SocketError /*sError*/) {
Q_Q(QModbusTcpServer);