基于 QudpSocket类编写的上位机,用于接收dsp的数据,内容如下。
可以看到要实现实现接收UDP报文有两步,一是使用bind()绑定地址和端口,二是调用redatagram()或者receivedatagram()接收报文。关于第二步,每当数据报文到达时,会发出readyread()信号且此时haspengingDataGrams()会返回true。
上代码
widget.h
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include <QUdpSocket>
QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
private slots:
void Device_info_readyRead();
void on_pushButton_bind_clicked();
private:
Ui::Widget *ui;
QUdpSocket *udpSocket_net;
};
#endif // WIDGET_H
widget.c
由于是单播通信,所以就直接再代码中固定IP与端口了.上位机IP:192.169.0.54,端口:8080。需要注意的是由于发送端是按照16进制发送,而非按照ascii发送,所以在将报文输出在textedit中需要按照16进制显示。 即ui->dataLabel->insertPlainText(dataGram.toHex()+"\n");否则由于数值超过了127将无法正常显示。
#include "widget.h"
#include "ui_widget.h"
#include <QUdpSocket>
#include <QDebug>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this); //初始化界面
ui->dataLabel->setReadOnly(true);//textedit设置为只读
udpSocket_net = new QUdpSocket;
}
Widget::~Widget()
{
delete ui;
}
void Widget::Device_info_readyRead()
{
while(udpSocket_net->hasPendingDatagrams())
{
QByteArray dataGram;
QHostAddress tempHost("192.168.0.54");
quint16 port = 28000;
dataGram.resize(udpSocket_net->pendingDatagramSize()); //读取的数据报大小
qDebug() << "开始监听:数据长度为" << dataGram.size() << endl;
udpSocket_net->readDatagram(dataGram.data(),dataGram.size(),&tempHost,&port);//读取下位机udp数据
ui->dataLabel->insertPlainText(dataGram.toHex()+"\n");//按16进制显示在dataLabel中
}
}
bool bindviewflag=0;
void Widget::on_pushButton_bind_clicked()
{
if (bindviewflag==0)
{
bindviewflag=1;
if(true == udpSocket_net->bind(QHostAddress("192.168.0.54"), 8080, QAbstractSocket::ShareAddress))
{
qDebug() << "绑定成功" << endl;
connect(udpSocket_net,SIGNAL(readyRead()),this,SLOT(Device_info_readyRead()));//连接信号与槽
ui->pushButton_bind->setText("关闭网络");
}
}
else
{
udpSocket_net->close();
ui->pushButton_bind->setText("打开网络");
bindviewflag=0;
}
}
ui界面,有点简陋:)
测试结果:
这里我让dsp每个控制周期发送的变量都为常数(1,2,3,4,5),每30个控制周期发送一次,一个浮点数是4个字节,4*5*30对应报文的长度是600,与调试的结果一致。
再附上一段QT的自写大小端转换的函数,用于将16进制浮点数按照大端读取。两个for循环是因为我的下位机28388,会将每个控制周期的变量值进行排序,每30个控制周期发送一次。所以写了两个for循环解码,代码如下:
int num=5;
float *ptr=new float[num*30]; //动态数组用于存放解码后的数据,动态数组的大小
void Widget::hex_to_single() //网络报文解码函数
{
int i,j;
for(i=0;i<30;i++){
for(j=0;j<num;j++){
QByteArray getdata=dataGram.toHex().mid(i*num*8+j*8,8); //获取对应的数值
qDebug()<<"字符串"<< getdata <<endl;
quint32 value = getdata.toUInt(0,16); //将字符串转换为数值 注意必须得转成uint
qDebug()<<value<<endl;
quint32 value2 = qToBigEndian(value); //小端字节序转大端字节序
qDebug()<<value2<<endl;
float b =*(float*)&value2; //转换为single类型
ptr[j*30+i]=b;
qDebug()<<ptr[j*30+i]<<endl;
}
}
}
追更:应该是QUdpSocket类与windows兼容度不够,超过1kHz的报文就接收不到了。追求更高的抓包速率推荐调用winpcap或npcap库。