TDMS文件:

TDMS文件是NI主推的一种二进制记录文件,TDMS文件由三个层次结构级别组成:文件、组、通道。在NI的LabVIEW软件中,可通过许多接口直接访问NI TDMS文件,但使用LABVIEW过于局限,NI提供了TDM C DLL,该DLL包含必要的函数,可在能灵活启用DLL通信的任意应用程序开发环境中读写TDMS文件。

TDM C DLL下载地址:

https://www.ni.com/content/dam/web/product-documentation/c_dll_tdm.zip

下载后

其中包含了头文件以及静态库等,由于我使用QT开发软件,故使用C++对其二次封装,实现动态添加组以及动态写入指定通道数据功能。

封装后的头文件:

其实说到低,对TDMS文件的操作,其实就是对文件指针的操作,我们打开NI提供的头文件nilibddc.h,如下图:

通过DDCFileHandle对文件进行操作,通过DDCChannelGroupHandle对组进行操作,通过DDCChannelHandle对通道进行操作。所以使用C++对其二次封装(QList是Qt提供的,如果使用c++请使用STL的List),便于操作:

#ifndef TDMSDATATYPEDEF_H
#define TDMSDATATYPEDEF_H// Qt VS 中文兼容(UTF-8)
#ifdef _MSC_VER
#pragma execution_character_set("utf-8")
#endif#include <QString>
#include <QList>
#include <QDateTime>#include "QNiTdms/include/nilibddc.h"// 定义:通道
struct TdmsStructChannel
{QString name;QString description;QString unit;DDCDataType dataType;DDCChannelHandle pChannel;
};// 定义:组
struct TdmsStructGroup
{QString name;QString description;DDCChannelGroupHandle pGroup;QList<TdmsStructChannel> channels;
};// 定义:文件
struct TdmsStructFile
{QString name;QString description;QString title;QString author;DDCFileHandle pFile;QList<TdmsStructGroup> groups;
};// 定义:通道 ---- 不含指针
struct TdmsInfoChannel
{QString name;QString description;QString unit;DDCDataType dataType;
};// 定义:组 ---- 不含指针
struct TdmsInfoGroup
{QString name;QString description;QList<TdmsInfoChannel> channels;
};// 定义:文件 ---- 不含指针
struct TdmsInfoFile
{QString name;QString description;QString title;QString author;QList<TdmsInfoGroup> groups;
};#endif // TDMSDATATYPEDEF_H

写入类封装:

#ifndef QTDMSWRITER_H
#define QTDMSWRITER_H// Qt VS 中文兼容(UTF-8)
#ifdef _MSC_VER
#pragma execution_character_set("utf-8")
#endif#include <QObject>
#include <QFileInfo>
#include <QDir>
#include <QVector>#include "tdmsdatatypedef.h"class QTdmsWriter : public QObject
{Q_OBJECT
public:explicit QTdmsWriter(QObject * parent = nullptr);explicit QTdmsWriter(const QString & filepath,QObject * parent = nullptr);explicit QTdmsWriter(const QString & filepath, const TdmsInfoFile & tdmsinfo, QObject * parent = nullptr);~QTdmsWriter();private:bool flagOpen;             // 状态指示:文件是否已经打开,true表示打开bool flagErrorOccurred;    // 状态指示:是否出现了错误,true表示出错QString errorMsg;          // 出错信息描述TdmsStructFile tdmsStruct;private:bool createFile(const QString & filePath, const QString & fileName, const QString & fileDescription, const QString & fileTitle, const QString & fileAuthor, DDCFileHandle & file);    // 创建文件bool addChannelGroup(DDCFileHandle file, const QString & groupName, const QString & groupDescription, DDCChannelGroupHandle & channelGroup);    // 添加组bool addChannel(DDCChannelGroupHandle channelGroup, DDCDataType dataType, const QString & channelName, const QString & channelDescription, const QString & channelUnit, DDCChannelHandle & channel);    // 添加通道bool appendChannelDataValuesUInt8(DDCChannelHandle channel, const QByteArray & ba, int & numValues);        // 添加通道数据:UInt8bool appendChannelDataValuesInt16(DDCChannelHandle channel, const QByteArray & ba, int & numValues);        // 添加通道数据:Int16bool appendChannelDataValuesInt32(DDCChannelHandle channel, const QByteArray & ba, int & numValues);        // 添加通道数据:Int32bool appendChannelDataValuesFloat(DDCChannelHandle channel, const QByteArray & ba, int & numValues);        // 添加通道数据:Floatbool appendChannelDataValuesDouble(DDCChannelHandle channel, const QByteArray & ba, int & numValues);       // 添加通道数据:Doublebool appendChannelDataValuesString(DDCChannelHandle channel, const QByteArray & ba, int & numValues);       // 添加通道数据:Stringbool appendChannelDataValuesTimestamp(DDCChannelHandle channel, const QByteArray & ba, int & numValues);    // 添加通道数据:Timestamppublic:bool isOpen() const;bool isErrorOccurred() const;QString errorDescription();bool appendDataValuesDoubleOfChannelGroup(int indexOfChannelGroup, const QList<std::vector<double>> & groupData);    // 追加保存:某“组”中所有通道的数据 ---- floatbool appendDataValuesOfChannelGroup(int indexOfChannelGroup, const QList<QByteArray> & groupData);    // 追加保存:某“组”中所有通道的数据//动态添加组bool addGroup(TdmsStructGroup groupInfo);//完成所有操作后保存bool saveFile();//向某组的某通道追加保存bool appendDataValuesDoubleOfGroupChannel(int indexOfChannelGroup,int indexOfChannel,QVector<double> &channelData);};#endif // QTDMSWRITER_H

其中私有成员中包含TdmsStructFile这个结构体,整个类的成员函数都是通过对这个结构体的操作来进行文件、组、通道的创建、写入。

创建TDMS文件:

// 创建文件
bool QTdmsWriter::createFile(const QString & filePath, const QString & fileName, const QString & fileDescription, const QString & fileTitle, const QString & fileAuthor, DDCFileHandle & file)
{// 1. 查看文件名,以tdm或tdms为后缀,自动判断if (QFile::exists(filePath))    // 文件已存在,删除之{bool flag = QFile::remove(filePath);if (!flag){flagErrorOccurred = true;errorMsg = tr("同名文件已存在,将其删除时出错");return false;}}QFileInfo tmpfile(filePath);QString strSuffix = tmpfile.suffix().toUpper();    // 后缀名(注意不含“.”)bool tdmsflag = ((strSuffix == "TDM") || (strSuffix == "TDMS"));if (!tdmsflag){flagErrorOccurred = true;errorMsg = tr("文件后缀名不是TDM或TDMS");return false;}// 2. 创建文件QString tdms_filepath = QDir::toNativeSeparators(filePath);std::string str_filepath = tdms_filepath.toLocal8Bit().toStdString();const char * ch_filepath = str_filepath.c_str();std::string str_file_name = fileName.toLocal8Bit().toStdString();const char * ch_file_name = str_file_name.c_str();std::string str_file_description = fileDescription.toLocal8Bit().toStdString();const char * ch_file_description = str_file_description.c_str();std::string str_file_title = fileTitle.toLocal8Bit().toStdString();const char * ch_file_title = str_file_title.c_str();std::string str_file_author = fileAuthor.toLocal8Bit().toStdString();const char * ch_file_author = str_file_author.c_str();int errorCode;if ((strSuffix == "TDM")){errorCode = DDC_CreateFile(ch_filepath, DDC_FILE_TYPE_TDM, ch_file_name, ch_file_description, ch_file_title, ch_file_author, &file);}else{errorCode = DDC_CreateFile(ch_filepath, DDC_FILE_TYPE_TDM_STREAMING, ch_file_name, ch_file_description, ch_file_title, ch_file_author, &file);}flagErrorOccurred = (errorCode != DDC_NoError);errorMsg = QString(DDC_GetLibraryErrorDescription(errorCode));if (flagErrorOccurred){return false;}if (file == nullptr)    // 进一步检查确认{flagErrorOccurred = true;errorMsg = tr("文件创建失败");return false;}return true;
}

动态添加组addGroup函数

bool QTdmsWriter::addGroup(TdmsStructGroup groupInfo)
{groupInfo.pGroup = nullptr;for(int i = 0 ; i < groupInfo.channels.size() ; i++){groupInfo.channels[i].pChannel = nullptr;}int errorCode;tdmsStruct.groups.append(groupInfo);int index = tdmsStruct.groups.size()-1;bool flag = false;bool tdmsFlag = false;flag = addChannelGroup(tdmsStruct.pFile,tdmsStruct.groups.at(index).name,tdmsStruct.groups.at(index).description,tdmsStruct.groups[index].pGroup);if(!flag){tdmsFlag = true;}for(int i = 0;i < tdmsStruct.groups.at(index).channels.size() ; i++){flag = addChannel(tdmsStruct.groups.at(index).pGroup,tdmsStruct.groups.at(index).channels.at(i).dataType,tdmsStruct.groups.at(index).channels.at(i).name,tdmsStruct.groups.at(index).channels.at(i).description,tdmsStruct.groups.at(index).channels.at(i).unit,tdmsStruct.groups[index].channels[i].pChannel);if(!flag){tdmsFlag = true;continue;}}if (tdmsFlag)    // 如果出错,关闭文件{int errcode = DDC_CloseFile(tdmsStruct.pFile);if (errcode == DDC_NoError){flagOpen = false;    // 已经顺利关闭}flagErrorOccurred = true;errorMsg = tr("TDM(S)文件中增添'组'或'通道'时出错,部分未成功");return false;}return true;
}

向某组的某通道写入数据appendDataValuesDoubleOfGroupChannel函数

bool QTdmsWriter::appendDataValuesDoubleOfGroupChannel(int indexOfChannelGroup, int indexOfChannel, QVector<double> &channelData)
{if (!flagOpen){return false;}int numGroups = tdmsStruct.groups.size();if ((indexOfChannelGroup < 0) || (indexOfChannelGroup > numGroups)){flagErrorOccurred = true;errorMsg = tr("输入的 ChannelGroup 索引号有误,超出了有效范围");return false;}int numChannels = tdmsStruct.groups.at(indexOfChannelGroup).channels.size();if ((indexOfChannel < 0) || (indexOfChannel > numChannels)){flagErrorOccurred = true;errorMsg = tr("输入的通道数据有误,与TDM(S)文件中该组的通道数量不一致");return false;}// 遍历所有通道bool tdmsflag = false;unsigned long long len = channelData.size();const double * pVals = channelData.data();double * ptr = const_cast<double *>(pVals);int errorCode = DDC_AppendDataValuesDouble(tdmsStruct.groups.at(indexOfChannelGroup).channels.at(indexOfChannel).pChannel, ptr, len);flagErrorOccurred = (errorCode != DDC_NoError);errorMsg = QString(DDC_GetLibraryErrorDescription(errorCode));if (flagErrorOccurred){tdmsflag = true;}if (tdmsflag){flagErrorOccurred = true;errorMsg = tr("TDM(S)文件中添加保存'通道'数据时出错,部分未成功");return false;}return true;
}

注意最后要保存 saveFile函数

bool QTdmsWriter::saveFile()
{int errorCode;errorCode = DDC_SaveFile(tdmsStruct.pFile);flagErrorOccurred = (errorCode != DDC_NoError);errorMsg = QString(DDC_GetLibraryErrorDescription(errorCode));if (flagErrorOccurred){return false;}return true;
}

写的Demo测试:

int main(int argc, char *argv[])
{QTdmsWriter *writer;//要写入通道的数据QVector<double> arr;for(int i =  0 ; i < 100 ; i++){arr.push_back(i);}//创建TDMS文件QDateTime startTime = QDateTime::currentDateTime();QString timeStr = startTime.toString("yyyyMMddhhmmss");//注意修改路径QString charaFilePathName = "E:/Demo/"+timeStr+".tdms";//TDMS文件信息TdmsInfoFile tdmsinfo;tdmsinfo.name = "ADQ";tdmsinfo.description = "save acquisition data";tdmsinfo.title = "data";tdmsinfo.author = "zx";//创建writer = new QTdmsWriter(charaFilePathName,tdmsinfo);//这里创建8个组,每个组8个通道for(int cnt = 0 ; cnt < 8 ; cnt++){//组名使用时间命名QDateTime curDateTime = QDateTime::currentDateTime();QString timeStr = curDateTime.toString("yyyyMMddhhmmss")+QString::number(cnt);QString groupName = timeStr ;TdmsStructGroup groupInfo ;groupInfo.name = groupName;//创建通道TdmsStructChannel channel;channel.name = "HP";channel.dataType = DDC_Double;channel.unit = "MPa";//添加到组中groupInfo.channels.append(channel);//通道名CP,物理量兆帕,类型为Doublechannel.name = "CP";channel.dataType = DDC_Double;channel.unit = "MPa";groupInfo.channels.append(channel);channel.name = "GP";channel.dataType = DDC_Double;channel.unit = "MPa";groupInfo.channels.append(channel);channel.name = "PT";channel.dataType = DDC_Double;channel.unit = "℃";groupInfo.channels.append(channel);channel.name = "HN";channel.dataType = DDC_Double;channel.unit = "MPa";groupInfo.channels.append(channel);channel.name = "CN";channel.dataType = DDC_Double;channel.unit = "MPa";groupInfo.channels.append(channel);channel.name = "GN";channel.dataType = DDC_Double;channel.unit = "MPa";groupInfo.channels.append(channel);channel.name = "NT";channel.dataType = DDC_Double;channel.unit = "℃";groupInfo.channels.append(channel);//添加组if(!writer->addGroup(groupInfo)){qDebug()<<"add group error";qDebug()<<writer->errorDescription();}}//向0组的1号通道写入arrwriter->appendDataValuesDoubleOfGroupChannel(0,1,arr);//向0组的2号通道写入arrwriter->appendDataValuesDoubleOfGroupChannel(0,2,arr);//注意最后一定要保存writer->saveFile();delete  writer;return 0;
}

测试效果:

总结:

数据确实已经写入了进去,注意保存操作应该放到最后。

通过EXC打开TDMS插件下载地址:

TDM Excel Add-In for Microsoft Excel Download - NI

本文整个项目下载地址(包含读的操作):

注意编译完成后,请将bin目录下的32-bit或64-bit的动态库全部加入编译的目录下,否则无法运行!

使用Qt-C++对NI-TDMS库封装进行文件组通道创建数据写入读出-其它文档类资源-CSDN文库

Qt封装TDMS文件实现动态添加组以及动态写入通道数据相关推荐

  1. Vue中 动态添加class写法 动态静态clas混合

    Vue中 动态添加class写法 动态静态clas混合 示例 //fx,cont-block为静态class ob为动态class <div :class="[ 'fx','cont- ...

  2. Qt项目UI文件中新添加的控件在代码中不识别的问题

    ui->XXXX 在UI界面中新添加了控件,但是在代码中怎么也不出现,或者划红线 我看不少人说备份删除文件再添加,或者关闭QT再打开... 其实只要右键项目清除构建,然后重新构建就可以了, 这有 ...

  3. 动态添加综合布局---动态添加控件及将某XML动态加入到Activity显示(续)

    前言:以前曾写过一篇关于动态生成控件的文章<动态添加控件及将某XML动态加入到Activity显示>,比较浅显,对于RelativeLayout的相关布局设置方法及相对布局与线性布局的混合 ...

  4. 动态添加 ajax,ajax动态的添加内容【原创】

    以前只是见过很多动态添加内容的效果,这次还是自己第一次在项目里接触到呢,就简单记录下来了,希望对大家有帮助. 这里的效果是选中一个选择框,对应的内容区就会相应的出现对应的详细内容,取消选择框,对应的内 ...

  5. php动态添加查询,php动态添加url查询参数的方法,php动态url参数_PHP教程

    php动态添加url查询参数的方法,php动态url参数 本文实例讲述了php动态添加url查询参数的方法.分享给大家供大家参考.具体分析如下: 这段代码可以动态为url添加key-value查询参数 ...

  6. layui option 动态添加_layui select动态添加option的实例

    html 产品类别 轻松融 容易融 快乐融 增加产品类别 js //重新渲染表单 function renderForm(){ layui.use('form', function(){ var fo ...

  7. python动态添加类方法_Python 动态添加类方法

    习题: 1. Shape基类,要求所有子类都必须提供面积的计算,子类有三角形.矩形.圆. 2. 上题圆类的数据可序列化 第一种方法:使用Mixin多继承组合的方式,混入其它类的属性和方法 第二种方法: ...

  8. layui option 动态添加_layuiselect如何动态添加option

    这次给大家带来layui select如何动态添加option,layui select动态添加option的注意事项有哪些,下面就是实战案例,一起来看一下. html 产品类别 轻松融 容易融 快乐 ...

  9. Elastic-Job:动态添加任务,支持动态分片

    多情只有春庭月,犹为离人照落花. 概述 因项目中使用到定时任务,且服务部署多实例,因此需要解决定时任务重复执行的问题.即在同一时间点,每一个定时任务只在一个节点上执行.常见的开源方案,如 elasti ...

最新文章

  1. Python-接口自动化(二)
  2. AWS论剑Azure:安全组之争
  3. (0010) iOS 开发之UI布局兼容 4s/5/6/7 屏幕解决方案
  4. (转)angular进行md5加密 base64加密 哈希加密
  5. pycharm 如何跳转光标上次查看的位置 ctrl+alt+(←→)(前进和返回)
  6. linux fedora35更改开机默认等待时间timeout
  7. BOOST_MP11_VERSION宏用法的测试程序
  8. C语言满分解法:L1-041 寻找250 (10分)(解题报告)
  9. App-V5.0服务器部署
  10. Manuscript match— 找到最适合你文章的那个ta(之期刊杂志)
  11. 高通:蓝牙5.0将可同时连接两个设备
  12. Linux内核源码阅读之系统调用mmap()
  13. win10正常上网但是网络图标显示无连接,无法开启热点
  14. 心电自动分析技术综述-Phililps DXL / GE Marquette / Glasgow / HES Hannover / Mortara / 福田 / 光电...
  15. 计算机四级网络工程师(操作系统单选)- 知识点
  16. Ubuntu破解密码
  17. 联通光猫pt952g管理员密码获取 v1.0
  18. iphone测试内存读取速度软件,iPhone7内存不同读写速度有差? 实测差距惊人!
  19. [附源码]计算机毕业设计JAVAjsp教学辅助系统
  20. 俄罗斯方块【六种模式】【c语言】【史上最强】

热门文章

  1. IBMX60笔记本装LINUX,Thinkpad 全系列恢复盘,告别使用盗版和繁杂的系统安装过程,免费提...
  2. 糖尿病遗传风险检测挑战赛
  3. 技术积累 — SecureCRT串口工具及其使用说明
  4. R语言读取 文件 中文乱码,R语言画图 中文不显示
  5. 如何用MATLAB检测一张图片中某种颜色的占比
  6. ubuntu php连接mysql问题解决
  7. 高等数学:第二章 导数与微分(5)隐函数的导数,由参数方程所确定的函数的导数
  8. 常用SQL server 语句笔记
  9. 2023-05-29 用 fltk gui库编写一个打字练习程序
  10. RAMDISK 内存盘工具推荐