为基于网络udp通信的固件协议进行测试,用QT编写测试工具。其间遇到了一些问题,并逐个解决。特记录在此。
1. 因为基于网络协议,所以需要获取IP,方便发送并接收UDP数据。但因虚拟网卡等,所以需要筛选IP。
QHostAddress addr; QListlist = QNetworkInterface::allAddresses(); foreach (addr, list) { if(addr.protocol() == QAbstractSocket::IPv4Protocol){ if (addr.toString().contains("192.168")){ address = QHostAddress(addr.toString()); } } }
而在发送命令时,若是以广播方式发送,则也需要考虑使用哪块网卡进行广播。因此要先绑定ip。
udpCMDSocket = new QUdpSocket(this); udpCMDSocket->bind(QHostAddress(ip)); udpCMDSocket->writeDatagram(cmd.toLocal8Bit(), HubAddr, port);
2. 当在主UI上临时需要做高占用的任务如while循环时,可以用QApplication::processEvents()来响应界面上的待处理事件。
while (!binFile.atEnd() { QByteArray byte = binFile.read(len); 。。。 sendCMD2Port(cmd, HubAddr, port); Sleep(interval); QApplication::processEvents(); }
3. 打开文件,若已有路径显示已有路径
QString tmpBinfile; if (BinFilePath.size() == 0) tmpBinfile = QFileDialog::getOpenFileName(this, tr("Open Bin File"), ".", tr("Bin File(*.bin)")); else tmpBinfile = QFileDialog::getOpenFileName(this, tr("Open Bin File"), BinFilePath, tr("Bin File(*.bin)"));
4. 读取文件,生成QListWidget,并排序
bool CaseInsSort(const QString &info1, const QString &info2) { return info1.toLower() < info2.toLower(); } void loadCases() { QFile file(filename); if (file.open(QFile::ReadWrite|QIODevice::Text)){ QTextStream data(&file); QStringList fonts; QString line1; QString line2; while (!data.atEnd()){ line1 = data.readLine(); line2 = data.readLine(); fonts<addItems(fonts);//把各行添加到listwidget file.close(); } }
5. QListWidgetItem改名
void renameCase() { QListWidgetItem *caseItem = caseList->currentItem(); if (caseItem){ caseList->openPersistentEditor(caseItem); } }
6. 发送UDP命令
void sendCMD2Port(QString cmd, QHostAddress HubAddr, int port) { udpCMDSocket = new QUdpSocket(this); udpCMDSocket->writeDatagram(cmd.toLocal8Bit(), HubAddr, port); udpCMDSocket->close(); }
7. 监听线程独立于主线程,包括对监听数据处理都在run中
void listenPortThread::run()
{
QUdpSocket udpListenSocket;
bool result = udpListenSocket.bind(address, port,QUdpSocket::ShareAddress);//绑定到指定的端口号
if(!result)//若绑定不成功,给出出错信息
{
udpListenSocket.close();
stop();
return;
}
islisten = true;
m_bStop = false;
while(!m_bStop)
{
QByteArray array;
QHostAddress address;
quint16 port;
if (islisten){
while (udpListenSocket.hasPendingDatagrams()) {
array.resize(udpListenSocket.pendingDatagramSize());//根据可读数据来设置空间大小
udpListenSocket.readDatagram(array.data(),array.size(),&address,&port); //读取数据
QString str = array;
。。。
8.单次定时器控制,判断是否在时限内收到回复。到时间在timeout函数中判断。
QTimer::singleShot(1500,this,SLOT(timeout())); sendCMD2Port(cmd, HubAddr, port);
9. 解析收到的json回复,通过关键字查询结果
bool listenPortThread::parseJson(QByteArray array, QString *sendID, QString *result, QString key){ QJsonParseError jsonError; QJsonDocument doucment = QJsonDocument::fromJson(array, &jsonError); // 转化为 JSON 文档 if (!doucment.isNull() && (jsonError.error == QJsonParseError::NoError)) { // 解析未发生错误 if (doucment.isObject()) { // JSON 文档为对象 QJsonObject object = doucment.object(); // 转化为对象 if (!key.isEmpty() && object.contains(key)){ QJsonValue Jvalue = object.value(key); if (Jvalue.isString()) { QString strvalue = Jvalue.toString(); *result = strvalue; } }
10. 返回的json数据,包括base64数据时,解码并还原为float
QJsonValue Jdata = object1.value("data"); if (Jdata.isString()) { QString strdata = Jdata.toString(); QByteArray Hex = QByteArray::fromBase64(strdata.toLatin1()).toHex(); QString calidata = ""; for (int i=0; i<12; i++){ QString str =Hex.mid(i*8+6,2)+Hex.mid(i*8+4,2)+Hex.mid(i*8+2,2)+Hex.mid(i*8,2); //低位到高位 int iHex = str.toUInt(0,16); float fHex = *(float*)&iHex ; calidata += QString("%1, ").arg(fHex); } *result = calidata; }
11. 在pro文件中通过VERSION设定APP版本号,并在程序中获取APP版本号
写道
In the YourApp.pro:
VERSION = 0.0.0.1
DEFINES += APP_VERSION=\\\"$$VERSION\\\"
In the main.cpp:
#include
QCoreApplication::setApplicationVersion(QString(APP_VERSION));
Wherever else in your sources, e.g. in the imaginary controller.cpp:
#include
QString yourAppVersion = QCoreApplication::applicationVersion();
VERSION = 0.0.0.1
DEFINES += APP_VERSION=\\\"$$VERSION\\\"
In the main.cpp:
#include
QCoreApplication::setApplicationVersion(QString(APP_VERSION));
Wherever else in your sources, e.g. in the imaginary controller.cpp:
#include
QString yourAppVersion = QCoreApplication::applicationVersion();
12. 发布release版本程序,自动打包QT所需动态库
写道
Qt官方开发环境默认使用动态链接库方式,在发布生成的可执行程序时,我们需要复制一大堆动态库,如果自己去复制动态库,很可能丢三落四,导致程序在别的电脑里无法正常运行。 因此 Qt 官方开发环境里自带了一个部署工具来帮助开发者自动拷贝大部分的依赖库。
Windows开发环境下windeployqt工具 (如果你已经将Qt的bin目录加入PATH环境,那么你可以直接在命令行使用windeployqt调用.)。首先,将项目中的release文件中的可执行文件拷到一个新建的文件夹中,例如project.exe,用Qt自带的生成必备的dll文件的程序windeployqt,来把必要的动态库拷到该文件夹中,打开命令行,输入windeployqtproject.exe,这时候大部分的dll文件都自动拷贝过来了,但是如果项目还用了一些其他的SDK,比如OpenCV,Chartdir51等等,就需要手动将所需dll拷贝过来,如果不知道还需要哪些软件,可以用Dependency Walker来查看缺少哪些dll文件。
Windows开发环境下windeployqt工具 (如果你已经将Qt的bin目录加入PATH环境,那么你可以直接在命令行使用windeployqt调用.)。首先,将项目中的release文件中的可执行文件拷到一个新建的文件夹中,例如project.exe,用Qt自带的生成必备的dll文件的程序windeployqt,来把必要的动态库拷到该文件夹中,打开命令行,输入windeployqtproject.exe,这时候大部分的dll文件都自动拷贝过来了,但是如果项目还用了一些其他的SDK,比如OpenCV,Chartdir51等等,就需要手动将所需dll拷贝过来,如果不知道还需要哪些软件,可以用Dependency Walker来查看缺少哪些dll文件。
注意:如果发布的应用是Qt Quick Application应用,那么命令行需要加上QML的安装目录。命令中的D:\Qt\Qt5.5.1\qml是qml的安装目录,请换成你自己的qml安装目录!!!!!
windeployqt hello.exe --qmldir D:\Qt\Qt5.5.1\qml
接下来要高大上的朋友就可以用Enigma VirtualBox软件虚拟化工具将多个文件封装到应用程序主文件,从而制作成为单执行文件的绿色软件。