在QtC++中实现tdmsReader和tdmsWriter

基于nilibddc.dll,在QtC++中实现tdmsReader和tdmsWriter

TDMS文件将数据划分为三个不同层次的对象。最顶层由单个对象组成,其中包含了文件本身特有的信息,如作者或标题。每个文件可包含任意数量的组,每组又可包含任意数量的通道。
下图是利用本文tdmsWriter的程序生成的文件结构:
在这里插入图片描述
本文代码脱胎于以下文章中的代码,并做了改进与完善,但本文并未下载该文章的资源代码,仅参考了其文章中的代码。
“Qt封装TDMS文件实现动态添加组以及动态写入通道数据”

tdms writer

首先需要确保写出的文件能够被labview、DIAdem等程序读取,因此,先参考上述文章写了文件写入类。
定义文件、组、通道的头文件如下:

#include <QString>
#include <QList>
#include <QDateTime>
#include "nilibddc.h"
#define Wf_XName            "wf_xname"
#define Wf_XUnit            "wf_xunit_string"
#define Wf_Start_Time       "wf_start_time"
#define Wf_Offset           "wf_start_offset"
#define Wf_Increment        "wf_increment"
// 定义:通道
struct TdmsChannel
{
    QString name;
    QString description;
    QString unit;//Y轴数字的单位
    DDCDataType dataType;

    QString wf_xName;//X轴数字的名称
    QString wf_xUnit;//X轴数字的单位
    QDateTime wf_StartTime;//时间轴的初始日期时间值
    int wf_Offset;//第一个值距离开始时间有几个Increment
    double wf_Increment;//X轴的增量值,采样率的倒数

    DDCChannelHandle pChannel;
    long long dataLength = -1;//read的时候获取的本通道数据长度
};
// 定义:组
struct TdmsGroup
{
    QString name;
    QString description;

    DDCChannelGroupHandle pGroup;
    QList<TdmsChannel> channels;
};
// 定义:文件
struct TdmsFile
{
    QString name;
    QString description;
    QString title;
    QString author;

    DDCFileHandle pFile;
    void appendGroup(TdmsGroup& g)
    {
        int counter = 1;
        while (groupsNames.contains(g.name)) {
            g.name = QString("%1_%2").arg(g.name).arg(counter);
            counter++;
        }
        groups<<g;
        groupsNames<<g.name;
    }
    const QList<TdmsGroup> &getGroups()
    {return groups;}
private:
    QList<TdmsGroup> groups;
    QList<QString> groupsNames;
};

本文中将一个writer包装为一个类,对应于一个tdms文件的引用,并通过各种函数实现组的创建、通道的创建、数据的添加、属性的添加、文件的保存等操作。
这里要重点描述一下文件保存操作,实际使用该类的过程中,如流式数据的保存,主程序可以无数次调用DDC_SaveFile,只要不关闭文件,可以一直调用,将已有的数据写入文件中。
下面是tdms writer的头文件定义:

#include <QObject>
#include <QFileInfo>
#include <QDir>
#include <QVector>
#include <QThread>
#include "TdmsDataTypeDef.h"

class QTdmsWriter : public QObject
{
    Q_OBJECT
public:
    explicit QTdmsWriter(const QString & filePath, TdmsFile & tdmsInfo, QObject * parent = nullptr);
    ~QTdmsWriter();
private:
    bool flagOpen = false;             // 状态指示:文件是否已经打开,true表示打开
    bool flagErrorOccurred = false;    // 状态指示:是否出现了错误,true表示出错
    QString errorMsg = "No ERROR";          // 出错信息描述
    QString fileFullPath;
    TdmsFile &tdmsStruct;
private:
    void setErrorMsg(QString msg);
    bool checkErrAndReturn(int errcode);
    // 创建文件,返回处理后的文件名,或null
    QString createFile(const QString & filePath,
                    const QString & fileDescription, const QString & fileTitle,
                    const QString & fileAuthor, DDCFileHandle & file);
    bool addGroup2File(DDCFileHandle file, TdmsGroup &group);    // 添加组
    bool addChannel2Group(DDCChannelGroupHandle channelGroup, TdmsChannel &channel);    // 添加通道
public:
    bool isOpen() const;
    bool isErrorOccurred() const;
    QString errorDescription();
    //动态添加组
    bool addGroup(TdmsGroup &groupInfo);
    //完成所有操作后保存
    bool saveFile();
    //关闭文件
    bool closeFile();
    //向某组的某通道追加保存
    bool appendDouble2ChannelofGroup(int indexOfChannelGroup,int indexOfChannel,QVector<double> &channelData);
    bool appendString2ChannelofGroup(int indexOfChannelGroup,int indexOfChannel,QVector<QString> &channelData);
};

利用该类即可保存出labVIEW、DIAdem、tdmsAddon均可打开的文件。

tdms reader

后续本文又写了一个reader类,将上述文件打开,并读取的所有定义的属性、以及数据。目前包含了double、float、string类型的数据,其他的可以以此类推。
头文件如下:

#include <QObject>
#include <QFileInfo>
#include <QDir>
#include "TdmsDataTypeDef.h"
class QTdmsReader : public QObject
{
    Q_OBJECT
public:
    explicit QTdmsReader(const QString & filePath,QObject *parent = nullptr);
    TdmsFile readFile;
    bool flagErrorOccurred = false;    // 状态指示:是否出现了错误,true表示出错
    QString errorMsg = "No ERROR";
    QVector<char> readChar(TdmsChannel &ch, int index,int len);
    QVector<short> readShort(TdmsChannel &ch, int index,int len);
    QVector<long> readLong(TdmsChannel &ch, int index,int len);
    QVector<float> readFloat(TdmsChannel &ch, int index,int len);
    QVector<double> readDouble(TdmsChannel &ch, int index,int len);
    QVector<QString> readString(TdmsChannel &ch, int index,int len);
signals:
private:
    void setErrorMsg(QString msg);
    ///
    /// \brief checkErrAndReturn
    /// \param errcode
    /// true:一切正常
    /// false:出现错误
    /// \return
    ///
    bool checkErrAndReturn(int errcode);
    ///
    /// \brief this_GetFilePropertyString
    /// \param file
    /// \param property   // File property constants
    /// #define DDC_FILE_NAME               "name"            // Name
    /// #define DDC_FILE_DESCRIPTION        "description"      // Description
    /// #define DDC_FILE_TITLE              "title"            // Title
    /// #define DDC_FILE_AUTHOR             "author"         // Author
    /// #define DDC_FILE_DATETIME           "datetime"         // Date/Time
    /// \return
    ///
    QString this_GetFilePropertyString(DDCFileHandle file, const char *property);
    ///
    /// \brief this_GetGroupPropertyString
    /// \param group
    /// \param property   // ChannelGroup property constants
    /// #define DDC_CHANNELGROUP_NAME         "name"            // Name
    /// #define DDC_CHANNELGROUP_DESCRIPTION  "description"      // Description
    /// \return
    ///
    QString this_GetGroupPropertyString(DDCChannelGroupHandle group, const char *property);
    ///
    /// \brief this_GetChannelPropertyString
    /// \param channelH
    /// \param property   // Channel property constants
    /// #define DDC_CHANNEL_NAME            "name"            // Name
    /// #define DDC_CHANNEL_DESCRIPTION     "description"      // Description
    /// #define DDC_CHANNEL_UNIT_STRING     "unit_string"      // Unit String
    ///
    /// #define Wf_XName            "wf_xname"
    /// #define Wf_XUnit            "wf_xunit_string"
    /// #define Wf_Start_Time       "wf_start_time"
    /// #define Wf_Offset           "wf_start_offset"
    /// #define Wf_Increment        "wf_increment"
    /// \return
    ///
    QString this_GetChannelPropertyString(DDCChannelHandle channelH, const char *property);
    void init_Channel(DDCChannelHandle channelH, TdmsChannel &ch);
};

测试

最后在main函数中做了测试

writer测试

void testwriter()
{
    QTdmsWriter *writer;
    //要写入通道的数据
    QVector<double> arr;
    for(int i =  0 ; i < 20480 ; i++)
    {
        arr<<i;
    }
    QVector<QString> arrStr;
    arrStr<<"1-8899aassdd测试测试"<<"2-测试"<<"3-ssdd测"<<"4-aassdd99测"<<"5-aassdd"<<"6-aassdd"<<"7-\n"
            <<"8-8899aassdd测试测试"<<"9-测试"<<"10-ssdd测"<<"11-aassdd99测"<<"12-aassdd"<<"13-aassdd"<<"\n"
              <<"8899aassdd测试测试"<<"测试"<<"ssdd测"<<"aassdd99测"<<"aassdd"<<"aassdd"<<"\n";
    //注意修改路径
    QString charaFilePathName = "E:/tdmsFileDemo/ttt/test.tdms";

    //TDMS文件信息
    TdmsFile tdmsinfo;
    tdmsinfo.name = "daq";
    tdmsinfo.description = "save acquisition data";
    tdmsinfo.title = "underwater acousi...";
    tdmsinfo.author = "peter";

    int  gCNT =14;
    //创建
    writer = new QTdmsWriter(charaFilePathName,tdmsinfo);

    //这里创建8个组,每个组8个通道
    for(int cnt = 0 ; cnt < gCNT ; cnt++)
    {
        //组名使用时间命名
        QDateTime curDateTime = QDateTime::currentDateTime();
        QString timeStr = curDateTime.toString("yyyyMMddhhmmss")+"_第"+QString::number(cnt)+"组";
        QString groupName = timeStr ;
        TdmsGroup groupInfo ;
        groupInfo.name = groupName;
        groupInfo.description = "?///>>><<<**&&^^%$#@@~";
        int rate=204800;
        groupInfo.channels<<TdmsChannel{"HP","description0","MPa",DDC_Double,
                            "时间","s",QDateTime::currentDateTime(),0,1.0/rate,nullptr}
                          <<TdmsChannel{"压力瞬态响应6688ssqq","description0","MPa",DDC_Double,
                            "时间","s",QDateTime::currentDateTime(),0,1.0/rate,nullptr}
                          <<TdmsChannel{"GP","description0","MPa",DDC_Double,
                            "时间","s",QDateTime::currentDateTime(),0,1.0/rate,nullptr}
                          <<TdmsChannel{"出口段温度测量","description0","℃",DDC_Double,
                            "时间","s",QDateTime::currentDateTime(),0,1.0/rate,nullptr}
                          <<TdmsChannel{"HN","description0","MPa",DDC_Double,
                            "时间","s",QDateTime::currentDateTime(),0,1.0/rate,nullptr}
                          <<TdmsChannel{"电阻测量","description0","Ω",DDC_Double,
                            "时间","s",QDateTime::currentDateTime(),0,1.0/rate,nullptr}
                          <<TdmsChannel{"NT","description0","℃",DDC_Double,
                            "时间","s",QDateTime::currentDateTime(),0,1.0/rate,nullptr}
                          <<TdmsChannel{"QTSTRINGs","test 文本","℃",DDC_String,
                            "文本","s",QDateTime::currentDateTime(),0,1.0/rate,nullptr};
        //添加组
        if(!writer->addGroup(groupInfo))
        {
            qDebug()<<"add group error:"<<writer->errorDescription();
        }
    }
    for(int appendIndex = 0;appendIndex<8;appendIndex++){//模拟采样8次,可随便调整
        for(int gIndex = 0;gIndex<gCNT;gIndex++)
        {
            for(int cIndex = 0;cIndex<7;cIndex++)
            {
                writer->appendDouble2ChannelofGroup(gIndex,cIndex,arr);
            }
            writer->appendString2ChannelofGroup(gIndex,7,arrStr);
        }
        writer->saveFile();
    }

    //注意最后一定要保存
    writer->closeFile();
    delete  writer;
}

reader测试

void testreader()
{
    QTextStream cout(stdout);
    QTdmsReader reader("E:/tdmsFileDemo/ttt/test_7.tdms");
    TdmsFile &tdmsinfo = reader.readFile;
    const QList<TdmsGroup> & Gs = tdmsinfo.getGroups();
    //getdata from index with len
    //遍历打印每个通道前12个值和最后12个值
    int firstLen = 12,lastLen = 12;
    for(int groupi = 0;groupi<Gs.count();groupi++)
    {
        QList<TdmsChannel> chans = Gs[groupi].channels;
        cout <<"group name: "<<Gs[groupi].name<<";group des: "<<Gs[groupi].description<<"\n";
        for(int channeli=0;channeli<chans.count();channeli++)
        {
            cout<<"ch name:"<<chans[channeli].name<<"ch description:"<<chans[channeli].description
                <<"ch wf_xName:"<<chans[channeli].wf_xName
                <<"ch dataType:"<<chans[channeli].dataType
                <<"ch unit:"<<chans[channeli].unit<<"\n";
            //打印数据
            switch(chans[channeli].dataType)
            {
            case DDC_Double:{
                QVector<double> data1 = reader.readDouble(chans[channeli],0,firstLen);
                cout<<"first "<<firstLen<<"values\n";
                for(double i : data1)
                    cout<<i<<";";
                cout<<"\n";
                QVector<double> dataLast1 = reader.readDouble(chans[channeli],
                                                             chans[channeli].dataLength-lastLen,
                                                             lastLen);
                cout<<"last "<<lastLen<<"values\n";
                for(double i : dataLast1)
                    cout<<i<<";";
                cout<<"\n";}
                break;
            case DDC_Float:{
                QVector<float> data2 = reader.readFloat(chans[channeli],0,firstLen);
                cout<<"first "<<firstLen<<"values\n";
                for(float i : data2)
                    cout<<i<<";";
                cout<<"\n";
                QVector<float> dataLast2 = reader.readFloat(chans[channeli],
                                                             chans[channeli].dataLength-lastLen,
                                                             lastLen);
                cout<<"last "<<lastLen<<"values\n";
                for(float i : dataLast2)
                    cout<<i<<";";
                cout<<"\n";}
                break;
            case DDC_String:{
                QVector<QString> data3 = reader.readString(chans[channeli],0,firstLen);
                cout<<"first "<<firstLen<<"values\n";
                for(QString i : data3)
                    cout<<i<<";";
                cout<<"\n";
                QVector<QString> dataLast3 = reader.readString(chans[channeli],
                                                             chans[channeli].dataLength-lastLen,
                                                             lastLen);
                cout<<"last "<<lastLen<<"values\n";
                for(QString i : dataLast3)
                    cout<<i<<";";
                cout<<"\n";}
                break;
            case DDC_UInt8:{

            }break;
            case DDC_Int16:{

            }break;
            case DDC_Int32:{

            }break;

            case DDC_Timestamp:{

            }break;
            }
        }

    }
}

资源分享

基于原来文章中的资源太贵了,这里直接分享本文修改的writer和reader的实现如下
在这里插入图片描述

包含了c_dll_tdm以及本文所述代码

引用

[1]: NI TDMS File Format - What is a TDMS File?
[2]: 写入可数据管理的TDMS文件
[3]: TDMS文件格式内部结构
[4]: Qt封装TDMS文件实现动态添加组以及动态写入通道数据

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值