串口线程类
SlaveThread(从机线程)
目的:等待并响应来自主机的请求,然后发送预设的响应数据。 关键行为: 线程启动后,通过串口监听请求数据。 当检测到有数据可读时,读取请求数据。 读取完毕后,向串口写入预设的响应数据。 如果写入成功,通过信号 request(QString) 发送接收到的请求信息给外部监听者。 如果在读取请求或写入响应时发生超时,会触发 timeout(QString) 信号。 同步机制:使用 QMutex 保证线程安全,检查和更新串口号、等待超时时间以及响应内容,但没有使用 QWaitCondition,因为它是一个被动监听请求的线程,不需要主动唤醒机制。
SlaveThread (从机线程) 示例
假设在一个自动化仓库管理系统中,有多个库存检查站,每个站点装备了一个读取条形码的扫描枪作为从设备(Slave)。这些从设备并不主动发起操作,而是等待仓库管理系统(主设备)的指令来执行任务。
场景描述:任务:当一个物品进入或离开仓库时,仓库管理系统需要记录该物品的条形码信息。 流程:主设备(如中央服务器)通过网络向各个站点的从机发送请求,要求读取条形码。从机(SlaveThread实例)不断监听串口,一旦接收到请求,立刻激活扫描枪读取条形码,然后通过串口将条形码数据作为响应发回给主设备。 特点:在这个场景中,SlaveThread体现为被动响应设备的职责,等待主设备的指令,执行读取任务并反馈结果。
SlaveThread 更适用于那些需要持续监听并响应外部指令的场景,如传感器数据采集、远程控制设备的响应等。(只采集数据,并不需要主动发送请求,而是始终监听并响应数据的到来)
class SlaveThread : public QThread
{
Q_OBJECT
public:
explicit SlaveThread(QObject *parent = nullptr);//
~SlaveThread();
void startSlave(const QString &portName, int waitTimeout, const QString &response);//初始化串口及启动线程
signals:
void request(const QString &s);//请求消息
void error(const QString &s);//串口错误消息
void timeout(const QString &s);//串口连接超时消息
private:
void run() override;//串口通信
QString m_portName;
QString m_response;
int m_waitTimeout = 0;
QMutex m_mutex;
bool m_quit = false;
};
初始化串口类
void SlaveThread::startSlave(const QString &portName, int waitTimeout, const QString &response)
{
const QMutexLocker locker(&m_mutex);//初始化线程锁
m_portName = portName;//设置串口号
m_waitTimeout = waitTimeout;//设置串口的等待时间
m_response = response;//设置串口的相应参数
if (!isRunning())//判断线程是否运行
start();//启动线程
}
打开串口并发送数据
void SlaveThread::run()
{
bool currentPortNameChanged = false;
// 加锁以安全地访问和更新线程间共享的数据
m_mutex.lock();
// 获取并记录当前配置与上次是否有变
QString currentPortName;
if (currentPortName != m_portName) {
// 检查端口名是否变更
currentPortName = m_portName;
currentPortNameChanged = true; // 标记端口变更
}
int currentWaitTimeout = m_waitTimeout; // 获取超时时间
QString currentResponse = m_response; // 获取响应信息
m_mutex.unlock(); // 解锁,释放互斥锁
QSerialPort serial; // 创建QSerialPort对象用于串口通信
// 主循环,持续运行直到收到退出信号
while (!m_quit) {
// 如果端口设置发生变更
if (currentPortNameChanged) {
serial.close(); // 关闭旧端口
serial.setPortName(currentPortName); // 设置新端口名
// 尝试打开新的串口,失败则发送错误信号并退出
if (!serial.open(QIODevice::ReadWrite)) {
emit error