//---------------------------------------------------------------------------// #ifndef IMCCHANNEL #define IMCCHANNEL #include "imc_datatype.hpp" #include "imc_conversion.hpp" #include #include #include #include #include #if defined(__linux__) || defined(__APPLE__) #include #endif //---------------------------------------------------------------------------// namespace imc { // collect uuid's of blocks required for full channel reconstruction struct channel_env { // define unique identifer for channel_env std::string uuid_; // collect affiliate blocks for a single channel std::string CBuuid_, CGuuid_, CCuuid_, CNuuid_; std::string CDuuid_, CTuuid_, Cbuuid_, CPuuid_, CRuuid_, CSuuid_; std::string NTuuid_, NOuuid_, NLuuid_; // reset all members void reset() { uuid_.clear(); CBuuid_.clear(); CGuuid_.clear(); CCuuid_.clear(); CNuuid_.clear(); CDuuid_.clear(); CTuuid_.clear(); Cbuuid_.clear(); CPuuid_.clear(); CRuuid_.clear(); CSuuid_.clear(); NTuuid_.clear(); NOuuid_.clear(); NLuuid_.clear(); } // get info std::string get_info(int width = 20) { std::stringstream ss; ss< std::string joinvec(std::vector
myvec, unsigned long int limit = 10, int prec = 10, bool fixed = true) { // include entire list for limit = 0 unsigned long int myvecsize = (unsigned long int)myvec.size(); limit = (limit == 0) ? myvecsize : limit; std::stringstream ss; ss<<"["; if ( myvec.size() <= limit ) { for ( dt el: myvec ) { customize_stream(ss,prec,fixed); ss< 1 ) sumstr.pop_back(); sumstr += std::string("]"); return sumstr; } #if defined(__linux__) || defined(__APPLE__) // convert encoding of any descriptions, channel-names, units etc. class iconverter { std::string in_enc_, out_enc_; iconv_t cd_; size_t out_buffer_size_; public: iconverter(std::string in_enc, std::string out_enc, size_t out_buffer_size = 1024) : in_enc_(in_enc), out_enc_(out_enc), out_buffer_size_(out_buffer_size) { // allocate descriptor for character set conversion // (https://man7.org/linux/man-pages/man3/iconv_open.3.html) cd_ = iconv_open(out_enc.c_str(), in_enc.c_str()); if ( (iconv_t)-1 == cd_ ) { if ( errno == EINVAL ) { std::string errmsg = std::string("The encoding conversion from ") + in_enc + std::string(" to ") + out_enc + std::string(" is not supported by the implementation."); throw std::runtime_error(errmsg); } } } void convert(std::string &astring) { if ( astring.empty() ) return; std::vector in_buffer(astring.begin(),astring.end()); char *inbuf = &in_buffer[0]; size_t inbytes = in_buffer.size(); std::vector out_buffer(out_buffer_size_); char *outbuf = &out_buffer[0]; size_t outbytes = out_buffer.size(); // perform character set conversion // ( - https://man7.org/linux/man-pages/man3/iconv.3.html // - https://www.ibm.com/docs/en/zos/2.2.0?topic=functions-iconv-code-conversion ) while ( inbytes > 0 ) { size_t res = iconv(cd_,&inbuf,&inbytes,&outbuf,&outbytes); if ( (size_t)-1 == res ) { std::string errmsg; if ( errno == EILSEQ ) { errmsg = std::string("An invalid multibyte sequence is encountered in the input."); throw std::runtime_error(errmsg); } else if ( errno == EINVAL ) { errmsg = std::string("An incomplete multibyte sequence is encountered in the input") + std::string(" and the input byte sequence terminates after it."); } else if ( errno == E2BIG ) { errmsg = std::string("The output buffer has no more room for the next converted character."); } throw std::runtime_error(errmsg); } } std::string outstring(out_buffer.begin(),out_buffer.end()-outbytes); astring = outstring; } }; #elif defined(__WIN32__) || defined(_WIN32) class iconverter { public: iconverter(std::string in_enc, std::string out_enc, size_t out_buffer_size = 1024) {} void convert(std::string &astring) {} }; #endif // channel struct channel { // associated environment of blocks and map of blocks channel_env chnenv_; std::map* blocks_; std::vector* buffer_; // collect meta-data of channels according to env, // just everything valueable in here std::string uuid_; std::string name_, comment_; std::string origin_, origin_comment_, text_; std::chrono::system_clock::time_point trigger_time_, absolute_trigger_time_; double trigger_time_frac_secs_; std::string language_code_, codepage_; std::string yname_, yunit_; std::string xname_, xunit_; double xstepwidth_, xoffset_; int xprec_; // buffer and data int signbits_, num_bytes_; // unsigned long int byte_offset_; unsigned long int buffer_offset_, buffer_size_; long int addtime_; int datatp_; imc::datatype dattyp_; std::vector ydata_; std::vector xdata_; // range, factor and offset double factor_, offset_; // group reference the channel belongs to int group_index_; std::string group_uuid_, group_name_, group_comment_; // constructor takes channel's block environment channel(channel_env &chnenv, std::map* blocks, std::vector* buffer): chnenv_(chnenv), blocks_(blocks), buffer_(buffer), factor_(1.), offset_(0.), group_index_(-1) { // declare list of block parameters std::vector prms; // use uuid from CN block uuid_ = chnenv_.CNuuid_; // extract associated CB data if ( blocks_->count(chnenv_.CBuuid_) == 1 ) { prms = blocks_->at(chnenv_.CBuuid_).get_parameters(); group_index_ = std::stoi(blocks_->at(chnenv_.CBuuid_).get_parameter(prms[2])); group_name_ = blocks_->at(chnenv_.CBuuid_).get_parameter(prms[4]); group_comment_ = blocks_->at(chnenv_.CBuuid_).get_parameter(prms[6]); } // extract associated CT data if ( blocks_->count(chnenv_.CTuuid_) == 1 ) { prms = blocks_->at(chnenv_.CTuuid_).get_parameters(); text_ = blocks_->at(chnenv_.CTuuid_).get_parameter(prms[4]) + std::string(" - ") + blocks_->at(chnenv_.CTuuid_).get_parameter(prms[6]) + std::string(" - ") + blocks_->at(chnenv_.CTuuid_).get_parameter(prms[8]); } // extract associated CD data if ( blocks_->count(chnenv_.CDuuid_) == 1 ) { prms = blocks_->at(chnenv_.CDuuid_).get_parameters(); xstepwidth_ = std::stod(blocks_->at(chnenv_.CDuuid_).get_parameter(prms[2])); xunit_ = blocks_->at(chnenv_.CDuuid_).get_parameter(prms[5]); // TODO // xname_ = std::string("time"); // find appropriate precision for "xdata_" by means of "xstepwidth_" xprec_ = (xstepwidth_ > 0 ) ? (int)ceil(fabs(log10(xstepwidth_))) : 10; } // extract associated CP data if ( blocks_->count(chnenv_.CPuuid_) == 1 ) { prms = blocks_->at(chnenv_.CPuuid_).get_parameters(); num_bytes_ = std::stoi(blocks_->at(chnenv_.CPuuid_).get_parameter(prms[3])); datatp_ = std::stoi(blocks_->at(chnenv_.CPuuid_).get_parameter(prms[4])); signbits_ = std::stoi(blocks_->at(chnenv_.CPuuid_).get_parameter(prms[5])); // byte_offset_ = std::stoul(blocks_->at(chnenv_.CPuuid_).get_parameter(prms[7])); } // extract associated Cb data if ( blocks_->count(chnenv_.Cbuuid_) == 1 ) { prms = blocks_->at(chnenv_.Cbuuid_).get_parameters(); buffer_offset_ = std::stoul(blocks_->at(chnenv_.Cbuuid_).get_parameter(prms[6])); buffer_size_ = std::stoul(blocks_->at(chnenv_.Cbuuid_).get_parameter(prms[7])); xoffset_ = std::stod(blocks_->at(chnenv_.Cbuuid_).get_parameter(prms[11])); addtime_ = (long int)std::stod(blocks_->at(chnenv_.Cbuuid_).get_parameter(prms[12])); } // extract associated CR data if ( blocks_->count(chnenv_.CRuuid_) == 1 ) { prms = blocks_->at(chnenv_.CRuuid_).get_parameters(); factor_ = std::stod(blocks_->at(chnenv_.CRuuid_).get_parameter(prms[3])); offset_ = std::stod(blocks_->at(chnenv_.CRuuid_).get_parameter(prms[4])); yunit_ = blocks_->at(chnenv_.CRuuid_).get_parameter(prms[7]); } // extract associated CN data if ( blocks_->count(chnenv_.CNuuid_) == 1 ) { prms = blocks_->at(chnenv_.CNuuid_).get_parameters(); name_ = blocks_->at(chnenv_.CNuuid_).get_parameter(prms[6]); yname_ = name_; comment_ = blocks_->at(chnenv_.CNuuid_).get_parameter(prms[8]); // group_index_ = std::stoi(blocks_->at(chnenv_.CNuuid_).get_parameter(prms[2])); } // extract associated NO data if ( blocks_->count(chnenv_.NOuuid_) == 1 ) { prms = blocks_->at(chnenv_.NOuuid_).get_parameters(); origin_ = blocks_->at(chnenv_.NOuuid_).get_parameter(prms[4]); origin_comment_ = blocks_->at(chnenv_.NOuuid_).get_parameter(prms[6]); } // extract associated NL data // codepage: // - http://www.iana.org/assignments/character-sets/character-sets.xhtml // - https://de.wikipedia.org/wiki/Zeichensatztabelle // language-code: // - https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-lcid/a9eac961-e77d-41a6-90a5-ce1a8b0cdb9c?redirectedfrom=MSDN if ( blocks_->count(chnenv_.NLuuid_) == 1 ) { prms = blocks_->at(chnenv_.NLuuid_).get_parameters(); codepage_ = blocks_->at(chnenv_.NLuuid_).get_parameter(prms[2]); language_code_ = blocks_->at(chnenv_.NLuuid_).get_parameter(prms[3]); } // obtain NT data // - https://en.cppreference.com/w/cpp/chrono/c/tm // - https://en.cppreference.com/w/cpp/io/manip/put_time if ( blocks_->count(chnenv_.NTuuid_) == 1 ) { prms = blocks_->at(chnenv_.NTuuid_).get_parameters(); //std::tm tm{}; std::tm tms = std::tm(); tms.tm_mday = std::stoi(blocks_->at(chnenv_.NTuuid_).get_parameter(prms[2])); tms.tm_mon = std::stoi(blocks_->at(chnenv_.NTuuid_).get_parameter(prms[3])) - 1; tms.tm_year = std::stoi(blocks_->at(chnenv_.NTuuid_).get_parameter(prms[4])) - 1900; tms.tm_hour = std::stoi(blocks_->at(chnenv_.NTuuid_).get_parameter(prms[5])); tms.tm_min = std::stoi(blocks_->at(chnenv_.NTuuid_).get_parameter(prms[6])); long double secs = std::stold(blocks_->at(chnenv_.NTuuid_).get_parameter(prms[7])); double secs_int; trigger_time_frac_secs_ = modf((double)secs,&secs_int); tms.tm_sec = (int)secs_int; //tms.tm_isdst = -1; // generate std::chrono::system_clock::time_point type // ( - https://www.gnu.org/software/libc/manual/html_node/Broken_002ddown-Time.html // - https://man7.org/linux/man-pages/man3/tzset.3.html ) std::time_t ts = timegm(&tms); //std::mktime(&tms); trigger_time_ = std::chrono::system_clock::from_time_t(ts); } // start converting binary buffer to imc::datatype if ( !chnenv_.CSuuid_.empty() ) convert_buffer(); // calculate absolute trigger-time absolute_trigger_time_ = trigger_time_ + std::chrono::seconds(addtime_); // + std::chrono::nanoseconds((long int)(trigger_time_frac_secs_*1.e9)); // convert any non-UTF-8 codepage to UTF-8 convert_encoding(); } // convert buffer to actual datatype void convert_buffer() { std::vector prms = blocks_->at(chnenv_.CSuuid_).get_parameters(); if ( prms.size() < 4) { throw std::runtime_error("CS block is invalid and features to few parameters"); } // extract (channel dependent) part of buffer unsigned long int buffstrt = prms[3].begin(); std::vector CSbuffer( buffer_->begin()+buffstrt+buffer_offset_+1, buffer_->begin()+buffstrt+buffer_offset_+buffer_size_+1 ); // determine number of values in buffer unsigned long int num_values = (unsigned long int)(CSbuffer.size()/(signbits_/8)); if ( num_values*(signbits_/8) != CSbuffer.size() ) { throw std::runtime_error("CSbuffer and significant bits of datatype don't match"); } // adjust size of ydata ydata_.resize(num_values); // distinguish numeric datatypes included in "imc_datatype" if ( datatp_ == 1 ) { imc::convert_data_to_type(CSbuffer,ydata_); } else if ( datatp_ == 2 ) { imc::convert_data_to_type(CSbuffer,ydata_); } else if ( datatp_ == 3 ) { imc::convert_data_to_type(CSbuffer,ydata_); } else if ( datatp_ == 4 ) { imc::convert_data_to_type(CSbuffer,ydata_); } else if ( datatp_ == 5 ) { imc::convert_data_to_type(CSbuffer,ydata_); } else if ( datatp_ == 6 ) { imc::convert_data_to_type(CSbuffer,ydata_); } else if ( datatp_ == 7 ) { imc::convert_data_to_type(CSbuffer,ydata_); } else if ( datatp_ == 8 ) { imc::convert_data_to_type(CSbuffer,ydata_); } // ... else if ( datatp_ == 11 ) { imc::convert_data_to_type(CSbuffer,ydata_); } else { throw std::runtime_error(std::string("unsupported/unknown datatype") + std::to_string(datatp_)); } // fill xdata_ for ( unsigned long int i = 0; i < num_values; i++ ) { xdata_.push_back(xoffset_+(double)i*xstepwidth_); } // employ data transformation if ( factor_ != 1.0 || offset_ != 0.0 ) { for ( imc::datatype& el: ydata_ ) { //std::cout<<"value:"<(ydata_,6,9,true)<<"\n" <(xdata_,6,xprec_,true)<<"\n"; // <(ydata_,0,9,true) <<",\"xdata\":"<(xdata_,0,xprec_,true); } // ss<<"\",\"aff. blocks\":\""<