Qt创建多线程接收惯导UDP数据

0 背景

项目需求,要用Qt接收惯导数据,数据采用UDP传输,在Qt中提供了QUdpSocket类来进行UDP数据报(datagrams)的发送和接收。这里我们还要了解一个名词Socket,也就是常说的“套接字”。 Socket简单地说,就是一个IP地址加一个port端口。因为我们要传输数据,就要知道往哪个机子上传送,而IP地址确定了一台主机,但是这台机子上可能运行着各种各样的网络程序,我们要往哪个程序中发送呢?这时就要使用一个端口来指定UDP程序。所以说,Socket指明了数据报传输的路径。

为了使惯导数据的接收不影响主页面的响应,创建新线程来不停地读取数据。下面创建一个UDP的接收端程序。新建一个mainwindow工程,在pro文件中添加QT += network,然后执行以下qmake,确保没有问题。新建gnss类,继承QThread,包括gnss.h和gnss.cpp文件。话不多说,直接上代码!

1 gnss.h

#ifndef GNSS_H
#define GNSS_H
#include <QThread>
#include <QtCore>
#include <QObject>
#include <QMutex>
#include <iostream>
#include <QtNetwork>

class GNSS: public QThread
{
public:    
    Q_OBJECT
public:
    GNSS();
    void stop();
private slots:
    void run();
    void processPendingDatagram();
private:
    bool stopped;
    QMutex m_lock;
    QUdpSocket *receiver;
    QFile f();
};

#endif // GNSS_H

2 gnss.cpp

#include "gnss.h"
#include <QTimer>
#include <QMutexLocker>
#include "mainwindow.h"

GNSS::GNSS()
{

    stopped = false;//标记可以运行
    //创建一个QUdpSocket类对象,该类提供了Udp的许多相关操作
    receiver = new QUdpSocket(this);
    int port =3000;//设置UDP的端口号参数,指定在此端口上监听数据
    //此处的bind是个重载函数,连接本机的port端口,采用ShareAddress模式(即允许其它的服务连接到相同的地址和端口,特别是
    //用在多客户端监听同一个服务器端口等时特别有效),和ReuseAddressHint模式(重新连接服务器)
    int receive = receiver->bind(QHostAddress("195.0.0.230"),port);
    qDebug() << "receive: " <<receive << endl;
    if(receive == 0)
    {
        qDebug() << "UDP Connected Succeed ! " << endl;
    }
    else
    {
        qDebug() << "UDP Connected Faild ! " << endl;
    }
}
void GNSS::stop()
{
    QMutexLocker locker(&m_lock);
    stopped = true;
    receiver->close();
    delete receiver;

}

void GNSS::run()
{
    //获得系统时间并输出
    QString min = QDateTime::currentDateTime().toString("yyyyMMddhhmm");
    //打开文本 以时间命名文件名字
    QString fileName = "C:\\SensorsData\\BASLER\\code\\build-opencv-Desktop_Qt_5_8_0_MSVC2015_64bit-Debug\\GNSS_" + min + ".txt";//假设指定文件夹路径为D盘根目录
    QFile f(fileName);
    LONGLONG time_now=0;
    QString s = QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss.zzz");
    QString a = QDateTime::currentDateTime().toString("yyyyMMddhhmm");
    QString b = QDateTime::currentDateTime().toString("mmss");

    while(!stopped)
    {
        while(receiver->hasPendingDatagrams())  //拥有等待的数据报
        {
           //qDebug() << "receive succeed ! " << endl;
            QByteArray datagram; //拥于存放接收的数据报
           //pendingDatagramSize为返回第一个在等待读取报文的size,
           //resize函数是把datagram的size归一化到参数size的大小一样
           datagram.resize(receiver->pendingDatagramSize());
           //接收数据报,将其存放到datagram中
           //将读取到的不大于datagram.size()大小数据输入到datagram.data()中,
           //datagram.data()返回的是一个字节数组中存储数据位置的指针
           receiver->readDatagram(datagram.data(),datagram.size());
           //将数据报内容显示出来
           QString HexData = datagram.toHex();
           //判断数据是否完整
           if(HexData.length() == 144)
           {
               //解析Hex数据
               QString Status = HexData.mid(42,2);
               qDebug() << "Status :" << Status  << endl;
               QString Latitude = HexData.mid(46,16);
               qDebug() << "Latitude :" << Latitude  << endl;
               QString Longitude = HexData.mid(62,16);
               qDebug() << "Longitude :" << Longitude  << endl;
               QString Altitude = HexData.mid(78,8);
               qDebug() << "Altitude :" << Altitude  << endl;
               QString North_Velocity = HexData.mid(86,6);
               qDebug() << "North_Velocity :" << North_Velocity  << endl;
               QString East_Velocity = HexData.mid(92,6);
               qDebug() << "East_Velocity :" << East_Velocity  << endl;
               QString Down_Velocity = HexData.mid(98,6);
               qDebug() << "Down_Velocity :" << Down_Velocity  << endl;
               QString Heading = HexData.mid(104,6);
               qDebug() << "Heading :" << Heading  << endl;
               QString Pitch = HexData.mid(110,6);
               qDebug() << "Pitch :" << Pitch  << endl;
               QString Roll = HexData.mid(116,6);
               qDebug() << "Roll :" << Roll  << endl;

               //获得系统时间并输出
               s = QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss.zzz");
               a = QDateTime::currentDateTime().toString("yyyyMMddhhmm");
               b = QDateTime::currentDateTime().toString("mmss");
               qDebug() << "min : " <<min<< endl;
               time_now = b.toLongLong();
               qDebug() << "time_now: " <<time_now<< endl;
               //每10分钟保存一次
               if(time_now % 1000 == 0)
               {
                   qDebug() << "write again ! " <<time_now<< endl;
                   f.fileName() = "C:\\SensorsData\\BASLER\\code\\build-opencv-Desktop_Qt_5_8_0_MSVC2015_64bit-Debug\\GNSS_" + a + ".txt";
               }

               qDebug() << "time" << s;

               if(!f.open( QIODevice::Append))
               {
                   cout << "Open failed." << endl;
               }
               QTextStream txtOutput(&f);
               txtOutput << s <<"$$"<< HexData<< "&&" << endl;

               f.close();

           }
           else
           {
               qDebug() << "The data is not complete !" << endl;
           }
        }
    }


}
/***********GNSS数据处理***********/
void GNSS::processPendingDatagram()
{

}

3 mainwindow.cpp

在ui界面新建两个button,分别是开始接收和停止接收,分别命名为StartGNSSBtn和StopGNSSBtn,然后在mainwindow.cpp中添加对应的槽函数

/***********开始GNSS数据接收***********/
void MainWindow::on_StartGNSSBtn_clicked()
{
    if(!gnss.isRunning())
    {
        gnss.start();
        ui->StartGNSSBtn->setEnabled(false);
        ui->StopGNSSBtn->setEnabled(true);
    }
    else
    {
        qDebug() << "GNSS receive has started !!!" << endl;

    }

}

/***********停止GNSS数据接收***********/
void MainWindow::on_StopGNSSBtn_clicked()
{
    if(gnss.isRunning())
    {
        gnss.stop();
    }
    ui->StartGNSSBtn->setEnabled(true);
    ui->StopGNSSBtn->setEnabled(false);
}

同时要记得在mainwindow.h中添加相关的变量和函数,添加头文件#include "gnss.h"

private:
    //GNSS相关对象
    GNSS gnss;
    QPushButton *StartGNSSBtn;
    QPushButton *StopGNSSBtn;
private slots:
    //GNSS相关的槽函数
    void on_StartGNSSBtn_clicked();
    void on_StopGNSSBtn_clicked();

至此完成在新的线程里接收GNSS数据并初步的分割保存工作,下一步进行解析及显示

相关推荐
©️2020 CSDN 皮肤主题: 游动-白 设计师:白松林 返回首页