Qt与下位机通信时,如何等待下位机回复和超时处理
在C++或Qt中实现与下位机(例如嵌入式设备、传感器等)的通信,并且需要等待对方回复,如果几秒后没有收到回复则执行下一步动作,可以使用多种方法来实现这种超时机制。以下是几种常见的实现方式:
1. 使用 QTimer 和 QEventLoopQTimer
结合 QEventLoop 可以方便地实现超时机制。QEventLoop 会阻塞当前线程,直到某个条件满足或超时发生。
示例代码
#include <QCoreApplication>
#include <QTimer>
#include <QEventLoop>
#include <QDebug>
void sendCommandAndWaitForResponse() {
// 假设这里发送命令到下位机
qDebug() << "Sending command to the device...";
QEventLoop loop;
QTimer timer;
timer.setSingleShot(true);
// 设置超时时间(例如5秒)
int timeout = 5000;
// 连接超时信号
QObject::connect(&timer, &QTimer::timeout, &loop, &QEventLoop::quit);
timer.start(timeout);
// 模拟接收响应
bool responseReceived = false; // 用于标记是否收到响应
// 启动事件循环,等待响应或超时
loop.exec();
if (!responseReceived) {
qDebug() << "Timeout: No response received within" << timeout / 1000 << "seconds.";
// 执行下一步动作
} else {
qDebug() << "Response received successfully.";
}
}
int main(int argc, char *argv[]) {
QCoreApplication a(argc, argv);
// 模拟异步响应
QTimer::singleShot(3000, []() {
qDebug() << "Simulating response from the device...";
// 在实际应用中,这里会设置 responseReceived 为 true
});
sendCommandAndWaitForResponse();
return a.exec();
}
2. 使用 QFuture 和 QFutureWatcher
QFuture 和 QFutureWatcher 可以用于异步操作,并且可以设置超时。
示例代码:
创建一个头文件 devicecommunication.h,并定义你的类:
#ifndef DEVICECOMMUNICATION_H
#define DEVICECOMMUNICATION_H
#include <QObject>
class DeviceCommunication : public QObject {
Q_OBJECT
public:
explicit DeviceCommunication(QObject *parent = nullptr);
void sendCommandAndWaitForResponse();
signals:
void responseReceived();
private slots:
void onResponseReceived();
};
#endif // DEVICECOMMUNICATION_H
创建一个源文件 devicecommunication.cpp,实现你的类的方法:
#include "devicecommunication.h"
#include <QCoreApplication>
#include <QFuture>
#include <QFutureWatcher>
#include <QTimer>
#include <QtConcurrent/QtConcurrent>
#include <QDebug>
DeviceCommunication::DeviceCommunication(QObject *parent) : QObject(parent) {}
void DeviceCommunication::sendCommandAndWaitForResponse() {
qDebug() << "Sending command to the device...";
QFuture<void> future = QtConcurrent::run([this] {
QThread::sleep(3); // 模拟响应时间
emit responseReceived(); // 发射信号表示响应已收到
});
QFutureWatcher<void> *watcher = new QFutureWatcher<void>(this);
connect(watcher, &QFutureWatcher<void>::finished, [this, watcher, future] {
if (future.isFinished()) {
qDebug() << "Response received successfully.";
watcher->deleteLater();
}
});
watcher->setFuture(future);
QTimer::singleShot(5000, [this, watcher] {
if (!watcher->isFinished()) {
qDebug() << "Timeout: No response received within 5 seconds.";
watcher->deleteLater();
}
});
}
void DeviceCommunication::onResponseReceived() {
qDebug() << "Response received, performing further actions...";
}
#include <QCoreApplication>
#include "devicecommunication.h"
int main(int argc, char *argv[]) {
QCoreApplication a(argc, argv);
DeviceCommunication communication;
QObject::connect(&communication, &DeviceCommunication::responseReceived, &communication, &DeviceCommunication::onResponseReceived);
communication.sendCommandAndWaitForResponse();
return a.exec();
}
3. 使用 QSocketNotifier 或 QIODevice
如果你是通过串口、TCP/IP 或其他 I/O 设备进行通信,可以使用 QSocketNotifier 或 QIODevice 的 waitForReadyRead 方法来等待数据,并结合 QTimer 实现超时。
示例代码(使用 QSerialPort)
#include <QCoreApplication>
#include <QSerialPort>
#include <QTimer>
#include <QDebug>
void sendCommandAndWaitForResponse(QSerialPort *serialPort) {
// 发送命令
serialPort->write("COMMAND\r\n");
qDebug() << "Sending command to the device...";
// 设置超时时间
int timeout = 5000;
QTimer timer;
timer.setSingleShot(true);
timer.start(timeout);
// 等待响应
while (true) {
if (serialPort->waitForReadyRead(100)) { // 100ms 轮询间隔
QByteArray response = serialPort->readAll();
qDebug() << "Response received:" << response;
break;
}
if (timer.isActive()) {
continue;
}
qDebug() << "Timeout: No response received within" << timeout / 1000 << "seconds.";
// 执行下一步动作
break;
}
}
int main(int argc, char *argv[]) {
QCoreApplication a(argc, argv);
// 初始化串口
QSerialPort serialPort;
serialPort.setPortName("COM1"); // 根据实际情况设置端口号
serialPort.setBaudRate(QSerialPort::Baud9600);
serialPort.setDataBits(QSerialPort::Data8);
serialPort.setParity(QSerialPort::NoParity);
serialPort.setStopBits(QSerialPort::OneStop);
serialPort.setFlowControl(QSerialPort::NoFlowControl);
if (!serialPort.open(QIODevice::ReadWrite)) {
qDebug() << "Failed to open serial port:" << serialPort.errorString();
return -1;
}
sendCommandAndWaitForResponse(&serialPort);
serialPort.close();
return a.exec();
}
4. 使用多线程和 std::condition_variable
如果你更喜欢使用标准C++库,可以使用多线程和 std::condition_variable 来实现超时机制。
示例代码
#include <iostream>
#include <thread>
#include <chrono>
#include <condition_variable>
#include <mutex>
std::condition_variable cv;
std::mutex mtx;
bool responseReceived = false;
void waitForResponse() {
std::unique_lock<std::mutex> lock(mtx);
cv.wait_for(lock, std::chrono::seconds(5), []{ return responseReceived; });
if (!responseReceived) {
std::cout << "Timeout: No response received within 5 seconds." << std::endl;
// 执行下一步动作
} else {
std::cout << "Response received successfully." << std::endl;
}
}
void simulateDeviceResponse() {
std::this_thread::sleep_for(std::chrono::seconds(3)); // 模拟响应时间
{
std::lock_guard<std::mutex> lock(mtx);
responseReceived = true;
}
cv.notify_one();
}
int main() {
std::cout << "Sending command to the device..." << std::endl;
std::thread t1(waitForResponse);
std::thread t2(simulateDeviceResponse);
t1.join();
t2.join();
return 0;
}
当然Qt库里面提供了对应的 QWaitCondition
总结
•QTimer 和 QEventLoop:适用于简单的超时机制。
•QFuture 和 QFutureWatcher:适用于复杂的异步操作。
•QSocketNotifier 或 QIODevice:适用于基于I/O的通信。
•多线程和 std::condition_variable:适用于标准C++库的超时机制