#ifndef TDM_RIPPER #define TDM_RIPPER #include #include #include #include #include #include #include #include #include #include #include "../pugixml/pugixml.hpp" class tdm_ripper { // .tdm and .tdx filenames std::string tdmfile_; std::string tdxfile_; // endianness (true = little, false = big) bool endianness_, machine_endianness_; // evtl. neglect groups with no actual channels bool neglect_empty_groups_; int num_empty_groups_; // number/names/ids of channels, channelgroups and channels's assignment to groups int num_channels_, num_groups_; std::vector channel_id_, inc_id_, units_, channel_name_; std::vector group_id_, group_name_; std::vector> group_timestamp_; std::vector num_channels_group_; std::vector channels_group_; std::vector channel_ext_; // minimum/maximum value in particular channel (is provided in .tdm file as float) std::vector> minmax_; // use xpointers and ids to assign channels to byteoffsets std::map xml_local_columns_, xml_values_, xml_double_sequence_; // byteoffset, length and datatype of channels std::vector byteoffset_; std::vector length_; std::vector type_; std::vector external_id_; // mapping of NI datatype to size (in bytes) of type std::map datatypes_; // xml parser pugi::xml_document xml_doc_; pugi::xml_parse_result xml_result_; // binary data container std::vector tdxbuf_; public: tdm_ripper(std::string tdmfile, std::string tdxfile = "", bool neglect_empty_groups = true); void parse_structure(); void list_channels(std::ostream& gout = std::cout, int width = 15, int maxshow = 50); void list_groups(std::ostream& gout = std::cout, int width = 15, int maxshow = 50); void show_structure(); // count number of occurences of substring in string int count_occ_string(std::string s, std::string sub) { int num_occs = 0; std::string::size_type pos = 0; while ( ( pos = s.find(sub,pos) ) != std::string::npos ) { num_occs++; pos += sub.length(); } return num_occs; } // obtain substring of 'entirestr' in between starting and stopping delimiter std::string get_str_between(std::string entirestr, std::string startlim, std::string stoplim) { std::size_t apos = entirestr.find(startlim); std::size_t bpos = entirestr.find_last_of(stoplim); assert( apos != std::string::npos && bpos != std::string::npos ); return entirestr.substr(apos+startlim.length(),bpos-(apos+startlim.length())); } void print_hash_local(const char* filename, int width = 20) { std::ofstream fout(filename); std::map::iterator it; int count = 0; for ( it = xml_local_columns_.begin(); it != xml_local_columns_.end(); it++ ) { count++; fout<first; fout<second; fout<<"\n"; } fout.close(); } void print_hash_values(const char* filename, int width = 20) { std::ofstream fout(filename); std::map::iterator it; int count = 0; for ( it = xml_values_.begin(); it != xml_values_.end(); it++ ) { count++; fout<first; fout<second; fout<<"\n"; } fout.close(); } void print_hash_double(const char* filename, int width = 20) { std::ofstream fout(filename); std::map::iterator it; int count = 0; for ( it = xml_double_sequence_.begin(); it != xml_double_sequence_.end(); it++ ) { count++; fout<first; fout<second; fout<<"\n"; } fout.close(); } void print_extid(const char* filename, int width = 20) { std::ofstream fout(filename); int count = 0; for ( auto extid: channel_ext_ ) { count++; fout<= 0 && groupid < num_groups_ ); return num_channels_group_[groupid]; } const std::string& channel_name(int channelid) { assert( channelid >= 0 && channelid < num_channels_ ); return channel_name_[channelid]; } // obtain overall channel id from combined group and group-specific channel id int obtain_channel_id(int groupid, int channelid) { assert( groupid >= 0 && groupid < num_groups_ ); assert( channelid >= 0 && channelid < num_channels_group_[groupid] ); // find cummulative number of channels int numsum = 0; for ( int i = 0; i < groupid; i++ ) { numsum += num_channels_group_[i]; } assert( (numsum + channelid) > 0 && (numsum + channelid) <= num_channels_ ); return numsum+channelid; } const std::string& channel_name(int groupid, int channelid) { return channel_name_[obtain_channel_id(groupid,channelid)]; } const std::string& group_name(int groupid) { assert( groupid >= 0 && groupid < num_groups_ ); return group_name_[groupid]; } const std::string& channel_unit(int groupid, int channelid) { return units_[obtain_channel_id(groupid,channelid)]; } int channel_exists(int groupid, std::string channel_name) { assert( groupid >= 0 && groupid < num_groups_ ); int channelid = -1; for ( int i = 0; i < num_channels_group_[groupid]; i++) { if ( comparestrings(channel_name_[obtain_channel_id(groupid,i)],channel_name) ) { channelid = i; } } return channelid; } bool comparestrings(std::string s1, std::string s2, bool case_sensitive = false) { if ( case_sensitive ) { return ( s1.compare(s2) == 0 ); } else { std::transform( s1.begin(), s1.end(), s1.begin(), ::tolower); std::transform( s2.begin(), s2.end(), s2.begin(), ::tolower); return ( s1.compare(s2) == 0 ); } } // evtl. get group time_stamp of .tdm file given in unix format std::string unix_timestamp(std::string unixts) { // average year of Gregorian calender const double avgdaysofyear = 365 + 1./4 - 1./100 + 1./400; // std::tm epochStart = {}; // epochStart.tm_sec = 0; // epochStart.tm_min = 0; // epochStart.tm_hour = 0; // epochStart.tm_mday = 1; // epochStart.tm_mon = 0; // epochStart.tm_year = 2040; // epochStart.tm_wday = 0; // epochStart.tm_yday = 0; // epochStart.tm_isdst = -1; // std::time_t epochtst = std::mktime(&epochStart); // convert string to long int = number of second since 0000/01/01 00:00 long int ts = atol(unixts.c_str()); // int year = (int)floor((double)ts/(avgdaysofyear*86400)); // int month = (int)floor((ts-year*avgdaysofyear*86400)/(30*86400)); // int day = (int)floor((ts-year*avgdaysofyear*86400-month*30*86400)/86400); // int daysec = ts-year*avgdaysofyear*86400-month*30*86400-day*86400; // int hour = (int)floor(daysec/3600.); // int mins = (int)floor((daysec-hour*3600)/60.); // int secs = (int)floor((daysec-hour*3600-mins*60)); // return std::to_string(year)+"-"+std::to_string(month)+"-"+std::to_string(day) // +" "+std::to_string(hour)+":"+std::to_string(mins)+":"+std::to_string(secs); // use STL to convert timestamp // std::time_t tstime = std::time_t(ts)-epochtst; std::time_t tstime = ts - 1970*avgdaysofyear*86400; return std::ctime(&tstime); } std::string time_stamp(int groupid, bool startstop = true) { assert( groupid >= 0 && groupid < num_groups_ ); return startstop ? unix_timestamp(group_timestamp_[groupid].first) : unix_timestamp(group_timestamp_[groupid].second); } void list_datatypes(); // convert array of chars to single integer or floating point double int convert_int(std::vector bych); double convert_double(std::vector bych); // disassemble single integer or double into array of chars std::vector convert_int(int number); std::vector convert_double(double number); // convert entire channel, i.e. expert of .tdx binary file // std::vector convert_channel(int byteoffset, int length, int typesize); std::vector convert_channel(int channelid); // obtain channel from overall channel id... std::vector get_channel(int channelid); // ...or from group id and group-specific channel id std::vector channel(int groupid, int channelid) { return get_channel(obtain_channel_id(groupid,channelid)); } int channel_length(int groupid, int channelid) { return length_[channel_ext_[obtain_channel_id(groupid,channelid)]]; } double get_min(int groupid, int channelid) { return minmax_[obtain_channel_id(groupid,channelid)].first; } double get_max(int groupid, int channelid) { return minmax_[obtain_channel_id(groupid,channelid)].second; } void print_channel(int channelid, const char* filename, int width = 15); // TODO add elements/methods to build .tdm and write .tdx files for your own data // by constructing xml document tree and write data to binary .tdx // void set_channels(std::vector channels); // void set_groups(std::vector groups); // void set_assigment(std::vector assignment); // void set_channel(int i, std::vector data); }; #endif