一、前言
在成功加载CAN接口函数库并打开CAN盒子的基础上,本文介绍如何在线程中完成报文的接收,报文格式的转化并通过TableWidget控件进行显示。编写过程参考了北京爱泰联合科技有限公司的DEMO http://www.itekon.com/download_2840.html
二、开辟子线程
因为是实时接受报文,比较占用资源,所以开辟一个线程,并在线程的run()函数中使用while循环来接收报文。
参考b站上面https://www.bilibili.com/video/BV1XW411x7NU?p=76线程开辟的方法,开辟一个子线程。
头文件如下:
#define MYTHREAD_H
#include <QThread>
#include"ControlCAN.h"
class MyThread : public QThread
{
Q_OBJECT
public:
explicit MyThread(QObject *parent = nullptr);
public:
void ReceiveCanData();
int closeFlag = 0;//接收数据
public:
void ReceiveCloseFlag(int close_Flag);
signals:
void ThreadSendCanData(QString strCANID,QString strFormat,QString strType,QString strLen,QString strData);//通过信号的方式,将子线程中的数据发送给主线程
void CloseThreadFlag();//子线程发送关闭子线程信号
void SendHydraulicSystemsInfo(float pressOfPowerReal,float pressOfPumpReal,float temperatureReal);
protected:
void run();
public slots:
};
#endif // MYTHREAD_H
源文件如下:
#include "mythread.h"
#include<QDebug>
#include<QString>
#include<QByteArray>
MyThread::MyThread(QObject *parent) : QThread(parent)
{
}
void MyThread::run()
{
ReceiveCanData();
}
三、接收数据
接收报文前需要定义报文接收的结构体VCI_CAN_OBJ frameinfo[1000];
通过VCI_Receive(设备编号,设备类型号(默认为0),CAN通道号,接收信息结构体,每一次接收个数,等待时间)函数接收数据。并通过QString定义的字符串,获得结构体中的ID,帧格式,帧类型,帧的数据长度(如果为数据帧),以及帧数据(如果为数据帧)。
源文件如下:
MyThread::MyThread(QObject *parent) : QThread(parent)
{
}
void MyThread::run()
{
ReceiveCanData();
}
void MyThread::ReceiveCanData()
{
VCI_CAN_OBJ frameinfo[1000];
//接收
// ULONG res = 0;
//获取缓冲区中接收但尚未被读取的帧数.devtype=3:设备类型USBCAN1;devind=0:设备索引号;canind=0:CAN通道0
//res = VCI_GetReceiveNum(20,0,0);
// qDebug()<<"帧数"<<res;
int len = 1;
while(true)
{
if(closeFlag == 1)
{
emit CloseThreadFlag();
break;
}
QString strCANID;
QString strFormat;
QString strType;
QString strLen;
QString strData;
len = VCI_Receive(20,0,0,frameinfo,50,200);//VCI_Receive(设备编号(20为VCI_USBCAN_E_U),设备类型号(默认为0),CAN通道号,接收信息结构体,每一次接收个数,等待时间)函数接收数据。
for(int i = 0; i < len; i++)
{
strCANID.clear();
strFormat.clear();
strData.clear();
strLen.clear();
strType.clear();
strCANID = QString("0x");
strCANID.append(QString::number(frameinfo[i].ID,16));
qDebug()<<strCANID;
//分析每一帧的格式,是数据帧还是远程帧,是标准帧还是扩展帧
if(frameinfo[i].RemoteFlag == 0)
{
strFormat = "数据帧";
}
else
{
strFormat = "远程帧";
}
if(frameinfo[i].ExternFlag == 0)
{
strType = "标准帧";
}
else
{
strType = "扩展帧";
}
//如果是数据帧,获取数据帧里面的数据
if(frameinfo[i].RemoteFlag == 0)
{
strLen = QString("%1").arg(frameinfo[i].DataLen);
for(int j = 0; j < frameinfo[i].DataLen; j++)
{
// tmpstr = QString("%02 ").arg(frameinfo[i].DataLen);
//QString tempStr = QString("%02").arg(frameinfo[i].Data[j]);
strData.append(QString::number(frameinfo[i].Data[j],16));//以16进制的方式将数据放入strData字符串中
//strData.append(tempStr);
strData.append(" ");//数据之间添加空格
}
}
emit ThreadSendCanData(strCANID,strFormat,strType,strLen,strData);
}
四、显示报文
通过相应按钮的槽函数开始子线程
程序如下:
thread0->start();
把子线程(thread0)发送的信号函数和主线程(this)中的槽函数连接(connect)
程序如下:
//连接线程发送数据信号和接受槽函数
connect(thread0,&MyThread::ThreadSendCanData,this,&Widget::ReceiveThreadDataAndShow);
在主窗口的构造函数中初始化表格窗口(tableWidget)对象(tableWidget为ui界面中显示数据的表格)
程序如下:
//为tablewidget设置格式
ui->tableWidget->setColumnCount(6);//设置列数,不设置行数,行数动态增加
//ui->tableWidget->setRowCount(1);
ui->tableWidget->setHorizontalHeaderLabels(QStringList()<<"时间"<<"帧ID"<<"帧格式"<<"帧类型"<<"数据长度"<<"数据(HEX)");
ui->tableWidget->setEditTriggers(QAbstractItemView::NoEditTriggers);//禁止修改表格中内容
// ui->tableWidget->setShowGrid(false);
ui->tableWidget->setColumnWidth(0,170);//设置第1列的宽度
ui->tableWidget->setColumnWidth(2,70);//设置第3列的宽度
ui->tableWidget->setColumnWidth(3,70);//设置第4列的宽度
ui->tableWidget->setColumnWidth(4,70);//设置第5列的宽度
ui->tableWidget->setColumnWidth(5,170);//设置第6列的宽度
接收报文并显示的槽函数,其中tableWidget为ui界面中显示数据的表格
程序如下:
//定义接受线程数据并显示的函数
void Widget::ReceiveThreadDataAndShow(QString strCANID, QString strFormat, QString strType, QString strLen, QString strData)
{
//获得系统当前时间
QDateTime time = QDateTime::currentDateTime();
QString strTime = time.toString("yyyy-MM-dd hh:mm:ss.zzz");
int rowCont;
rowCont = ui->tableWidget->rowCount();//获取当前表格中行数
ui->tableWidget->insertRow(rowCont);//在当前表格后插入一空行
ui->tableWidget->setItem(rowCont,0,new QTableWidgetItem(strTime));//给表格添加时间戳
ui->tableWidget->setItem(rowCont,1,new QTableWidgetItem(strCANID));//给表格添加ID
ui->tableWidget->item(rowCont,1)->setTextAlignment(Qt::AlignHCenter | Qt::AlignVCenter);//水平竖直居中
ui->tableWidget->setItem(rowCont,2,new QTableWidgetItem(strFormat));//给表格添加帧格式(数据帧还是远程帧)
ui->tableWidget->item(rowCont,2)->setTextAlignment(Qt::AlignHCenter | Qt::AlignVCenter);//水平竖直居中
ui->tableWidget->setItem(rowCont,3,new QTableWidgetItem(strType));//给表格添加帧类型
ui->tableWidget->item(rowCont,3)->setTextAlignment(Qt::AlignHCenter | Qt::AlignVCenter);//水平竖直居中
ui->tableWidget->setItem(rowCont,4,new QTableWidgetItem(strLen));//给表格添加报文数据长度
ui->tableWidget->item(rowCont,4)->setTextAlignment(Qt::AlignHCenter | Qt::AlignVCenter);//水平竖直居中
ui->tableWidget->setItem(rowCont,5,new QTableWidgetItem(strData));//给表格添加报文数据
//ui->tableWidget->item(0,0)->setTextAlignment(Qt::AlignHCenter);
ui->tableWidget->scrollToBottom();//滚动条处于最底部
ui->tableWidget->show();
}
总结
这里对文章进行总结:
1.接收报文数据需要用到 VCI_Receive()接口函数
2.接收数据需要在子线程中进行
3.显示数据需要用到ui界面中的Table Widget控件
注意:接收函数需要用到信息的结构体;
子线程的开启需要在主线程中的控件来完成;
TableWidget采用动态增加行的方式。