diff --git a/config/online_monitor.conf b/config/online_monitor.conf new file mode 100644 index 0000000000000000000000000000000000000000..da29f68cc146abcbb4a0d24e82c23fbf880b1983 --- /dev/null +++ b/config/online_monitor.conf @@ -0,0 +1,10 @@ +# configuration for online monitoring + +host = clrlpc.jlab.org +port = 11111 +etfile = /tmp/et_test +modules = ${THIS_DIR}/esb_module.conf +output = ${THIS_DIR}/../processed_data/online.root +update_interval = 2000 # ms + + diff --git a/include/utils.h b/include/utils.h index 7f8fbfad76719d416a5065a7c24d4938320debe7..1d3f4d27e1d2520f4935dfd8bbeb3d3495c97ae3 100644 --- a/include/utils.h +++ b/include/utils.h @@ -114,9 +114,9 @@ std::vector<Module> read_modules(const std::string &path, bool verbose=false) // module attributes Module mod; - mod.crate = conf.GetConfigValue("crate").Int(); - mod.slot = conf.GetConfigValue("slot").Int(); - mod.type = str2ModuleType(conf.GetConfigValue("type").c_str()); + mod.crate = conf.Value("crate").Int(); + mod.slot = conf.Value("slot").Int(); + mod.type = str2ModuleType(conf.Value("type").c_str()); // channels for (auto &sub : btext.blocks) { diff --git a/online/monitor.cxx b/online/monitor.cxx index 46af8c3c319636c18e962d713dc629c244573510..8a4c42f2d0a1aecbbd3342d6c7d3e0763017b882 100644 --- a/online/monitor.cxx +++ b/online/monitor.cxx @@ -17,7 +17,6 @@ #define FADC_BANK 3 -#define PROGRESS_COUNT 100 volatile sig_atomic_t term = 0; @@ -309,19 +308,29 @@ void processEvent(const uint32_t *buf, int &count, TTree *tree, bool &init_tree, } } -void monitor(const std::string &addr = "clrlpc", int port = 11111, const std::string &etfile = "/tmp/et_test", - const std::string &opath = "processed_data/online.root", - const std::string &module_conf = "config/esb_module.conf", - int nev = -1) +void monitor(const std::string &cpath = "config/online_monitor.conf") { - auto modules = read_modules(module_conf); + // read configuration + ConfigObject conf; + conf.ReadConfigFile(cpath); + auto host = conf.Value<std::string>("host", "clrlpc.jlab.org"); + auto port = conf.Value<int>("port", 11111); + auto etfile = conf.Value<std::string>("etfile", "/tmp/et_test"); + auto modconf = conf.Value<std::string>("modules", "config/esb_module.conf"); + auto up_intvl = conf.Value<double>("update_interval", 5000); + auto outfile = conf.Value<std::string>("output", "processed_data/online.root"); + auto nev = conf.Value<int>("number_events", -1); + + // connect et ETChannel et_chan(100, 100); - if (et_chan.Connect(addr.c_str(), port, etfile.c_str(), "TCD_MONITOR") != ET_OK) { + if (et_chan.Connect(host.c_str(), port, etfile.c_str(), "TCD_MONITOR") != ET_OK) { + std::cout << fmt::format("Failed to connect to the et system {}:{}:{}, abort monitoring!", host, port, etfile) + << std::endl; return; } + et_chan.Open(); - auto *hfile = new TFile(opath.c_str(), "RECREATE", "MAPMT test results"); - auto tree = new TTree("EvTree", "Cherenkov Test Events"); + auto modules = read_modules(modconf); // set up branches and a container to be combined with the branches std::unordered_map<std::string, BranchData> brdata; for (auto &mod : modules) { @@ -334,25 +343,34 @@ void monitor(const std::string &addr = "clrlpc", int port = 11111, const std::st } } + // root file and event tree + auto *hfile = new TFile(outfile.c_str(), "RECREATE", "MAPMT test results"); + auto tree = new TTree("EvTree", "Cherenkov Test Events"); + + // display plots auto dply = CreateMonitorDisplay("/online/latest/", 9100, tree); dply->InitAll(); + + // timer + auto timer = std::chrono::steady_clock::now(); + + // start monitoring int count = 0; - bool init_tree = false; int data_mode = -1; - int status = ETChannel::READ_EMPTY; - et_chan.Open(); - signal (SIGINT, handle_sig); - + bool init_tree = false; + signal(SIGINT, handle_sig); while (true) { if (term > 0 || (nev > 0 && nev < count)) { break; } - if (count % PROGRESS_COUNT == 0 && count > 0) { - std::cout << "Read and processed events - " << count << std::endl; + auto now = std::chrono::steady_clock::now(); + if (std::chrono::duration_cast<std::chrono::milliseconds>(now - timer).count() > up_intvl) { + timer = now; + std::cout << "Monitor update, processed events - " << count << std::endl; dply->Process(); dply->UpdateAll(); } - status = et_chan.ReadEvent(); + int status = et_chan.ReadEvent(); switch (status) { case ETChannel::READ_ERROR: diff --git a/rootlogon.C b/rootlogon.C new file mode 100644 index 0000000000000000000000000000000000000000..fbc218253cd3703d407784214e490c976ee6a209 --- /dev/null +++ b/rootlogon.C @@ -0,0 +1,10 @@ +{ + gInterpreter->AddIncludePath("./include"); + gInterpreter->AddIncludePath("./third_party/et_coda_3.10/include"); + gInterpreter->AddIncludePath("./third_party/simple"); + gInterpreter->AddIncludePath("./third_party/prconf/include"); + // gSystem->Load("libcoda_et.so"); + gSystem->Load("./third_party/et_coda_3.10/lib/libet.so"); + gSystem->Load("./third_party/prconf/libprconf.so"); + gSystem->Load("./third_party/simple/libsimple.so"); +} diff --git a/src/esb_analyze_online.cpp b/src/esb_analyze_online.cpp index 9cafff5d092cdcf907b4c9c74102e416b33a32d0..f7a896264a78ac536f2c11e468ec183ce417df22 100644 --- a/src/esb_analyze_online.cpp +++ b/src/esb_analyze_online.cpp @@ -6,10 +6,6 @@ #include "ConfigParser.h" #include "ConfigObject.h" #include "ConfigOption.h" -#include "THaCodaFile.h" -#include "THaEvData.h" -#include "CodaDecoder.h" -#include "evio.h" #include "TSpectrum.h" #include "TTree.h" #include "TFile.h" @@ -23,28 +19,17 @@ #define PROGRESS_COUNT 100 -volatile sig_atomic_t sig_caught = 0; +volatile sig_atomic_t terminate = 0; void handle_sig(int signum) { - /* in case we registered this handler for multiple signals */ - if (signum == SIGINT) { - sig_caught = 1; - } - if (signum == SIGTERM) { - sig_caught = 2; - } - if (signum == SIGABRT) { - sig_caught = 3; - } + terminate = 1; } -void write_raw_data(const std::string &addr, int port, const std::string &etfile, - const std::string &opath, int nev, const std::vector<Module> &modules); -bool parseEvent(const uint32_t *buf, bool verbose); -uint32_t parseBlock(const uint32_t *buf); +void monitor(const std::string &addr, int port, const std::string &etfile, + const std::string &opath, const std::string &modules_conf, int nev); uint32_t swap_endian32(uint32_t num) @@ -56,6 +41,21 @@ uint32_t swap_endian32(uint32_t num) return b0 | b1 | b2 | b3; } + +uint32_t parseBlock(const uint32_t *buf) +{ + auto header = (ETChannel::CodaEvHeader*) buf; + const uint32_t buf_size = header->length + 1; + std::cout << "ROC header: " << std::dec << buf_size << "\n" + << (int) header->etype << ", " << (int) header->dtype << ", " << (int) header->num + << std::endl; + std::cout << std::hex; + for (uint32_t i = 0; i < buf_size; ++i) { + std::cout << "0x" << std::setw(8) << std::setfill('0') << buf[i] << "\n"; + } + return buf_size; +} + // parse an evio event bool parseEvent(const uint32_t *buf, bool verbose = false) { @@ -99,21 +99,6 @@ bool parseEvent(const uint32_t *buf, bool verbose = false) } -uint32_t parseBlock(const uint32_t *buf) -{ - auto header = (ETChannel::CodaEvHeader*) buf; - const uint32_t buf_size = header->length + 1; - std::cout << "ROC header: " << std::dec << buf_size << "\n" - << (int) header->etype << ", " << (int) header->dtype << ", " << (int) header->num - << std::endl; - std::cout << std::hex; - for (uint32_t i = 0; i < buf_size; ++i) { - std::cout << "0x" << std::setw(8) << std::setfill('0') << buf[i] << "\n"; - } - return buf_size; -} - - int main(int argc, char* argv[]) { // setup input arguments @@ -166,9 +151,7 @@ int main(int argc, char* argv[]) } } - auto modules = read_modules(mconf); - - write_raw_data(host, port, etfile, conf_opt.GetArgument(0), nev, modules); + monitor(host, port, etfile, conf_opt.GetArgument(0), mconf, nev); } @@ -339,7 +322,7 @@ void fill_tree(TTree *tree, std::unordered_map<std::string, BranchData> &brdata, void processEvent(const uint32_t *buf, int &count, TTree *tree, bool &init_tree, int &data_mode, const std::vector<Module> &modules, std::unordered_map<std::string, BranchData> &brdata) { - if (!parseEvent(buf)) { + if (!parseEvent(buf, false)) { return; } @@ -388,15 +371,17 @@ void processEvent(const uint32_t *buf, int &count, TTree *tree, bool &init_tree, } -void write_raw_data(const std::string &addr, int port, const std::string &etfile, - const std::string &opath, int nev, const std::vector<Module> &modules) +void monitor(const std::string &addr, int port, const std::string &etfile, + const std::string &opath, const std::string &mconf, int nev) { ETChannel et_chan(100, 100); if (et_chan.Connect(addr.c_str(), port, etfile.c_str(), "TCD_MONITOR") != ET_OK) { return; } - auto *hfile = new TFile(opath.c_str(), "RECREATE", "MAPMT test results"); + auto modules = read_modules(mconf); + + auto *hfile = new TFile(opath.c_str(), "RECREATE", "MAPMT online monitor"); auto tree = new TTree("EvTree", "Cherenkov Test Events"); // set up branches and a container to be combined with the branches std::unordered_map<std::string, BranchData> brdata; @@ -413,26 +398,33 @@ void write_raw_data(const std::string &addr, int port, const std::string &etfile int count = 0; bool init_tree = false; int data_mode = -1; + int status = ETChannel::READ_EMPTY; et_chan.Open(); - int status = et_chan.ReadEvent(); signal (SIGINT, handle_sig); - while (status != ETChannel::READ_ERROR && status != ETChannel::READ_EOF) { - if (sig_caught || (nev > 0 && nev < count)) { break; } - // read-in data - if (status == ETChannel::READ_EMPTY) { - std::cout << "ET station is empty, wating 5 secs..." << std::endl; - std::this_thread::sleep_for(std::chrono::seconds(5)); - status = et_chan.ReadEvent(); - continue; - } - if((count % PROGRESS_COUNT) == 0) { + while (true) { + if (terminate > 0 || (nev > 0 && nev < count)) { break; } + if (count % PROGRESS_COUNT == 0) { std::cout << "Read and processed events - " << count << std::endl; } - processEvent(et_chan.GetEvBuffer(), count, tree, init_tree, data_mode, modules, brdata); status = et_chan.ReadEvent(); - } + + switch (status) { + case ETChannel::READ_ERROR: + case ETChannel::READ_EOF: + terminate = 1; + break; + case ETChannel::READ_EMPTY: + std::cout << "ET station is empty, wait 2 secs..." << std::endl; + std::this_thread::sleep_for(std::chrono::seconds(2)); + break; + case ETChannel::READ_OK: + processEvent(et_chan.GetEvBuffer(), count, tree, init_tree, data_mode, modules, brdata); + break; + } + } + std::cout << "Read and processed events - " << count << std::endl; hfile->Write(); diff --git a/third_party/prconf/include/ConfigObject.h b/third_party/prconf/include/ConfigObject.h index aa3bd1c82e8582cd994dca214d910b6edaf9076c..757f32eda3d1029ecdb142e57b91051938fd8814 100644 --- a/third_party/prconf/include/ConfigObject.h +++ b/third_party/prconf/include/ConfigObject.h @@ -8,17 +8,18 @@ #include "ConfigParser.h" -#define CONF_CONN(val, str, def, warn) val=GetConfigValue<decltype(val)>(str, def, warn) -#define CONF_CONN2(val, def, warn) val=GetConfiValue<decltype(val)>(str, def, warn) -#define GET_CONF(obj, val, str, def, warn) val=obj.GetConfiValue<decltype(val)>(str, def, warn) -#define GET_CONF2(obj, val, def, warn) val=GetConfiValue<decltype(val)>(#val, def, warn) +#define CONF_CONN(val, str, def, warn) val=Value<decltype(val)>(str, def, warn) +#define CONF_CONN2(val, def, warn) val=Value<decltype(val)>(str, def, warn) +#define GET_CONF(obj, val, str, def, warn) val=obj.Value<decltype(val)>(str, def, warn) +#define GET_CONF2(obj, val, def, warn) val=Value<decltype(val)>(#val, def, warn) class ConfigObject { public: // constructor, desctructor - ConfigObject(const std::string &spliiter = ":=", const std::string &ignore = " _\t", bool case_ins = true); + ConfigObject(const std::string &spliiter = ":=", const std::string &ignore = " _\t", + const std::string &var_open = "${", const std::string &var_close = "}", bool case_ins = true); virtual ~ConfigObject(); @@ -29,36 +30,37 @@ public: bool HasKey(const std::string &name) const; bool ReadConfigFile(const std::string &path); - void ReadConfigString(const std::string &content); + void ReadConfigString(const std::string &content, const std::string &path = "."); void SetConfigValue(const std::string &var_name, const ConfigValue &c_value); void SetIgnoreChars(const std::string &ignore) {ignore_chars = ignore;} void SetSplitChars(const std::string &splitter) {split_chars = splitter;} - void SetReplacePair(const std::string &open, const std::string &close) + void SetVariablePair(const std::string &open, const std::string &close) { - replace_pair = std::make_pair(open, close); + variable_pair = std::make_pair(open, close); } // get members - ConfigValue GetConfigValue(const std::string &var_name) const; + ConfigValue Value(const std::string &var_name) const; template<typename T> - T GetConfigValue(const std::string &var_name) + T Value(const std::string &var_name) const { - return GetConfigValue(var_name).Convert<T>(); + return Value(var_name).Convert<T>(); } - ConfigValue GetConfigValue(const std::string &var_name, const ConfigValue &def_value, bool verbose = true); + ConfigValue Value(const std::string &var_name, const ConfigValue &def_value, bool verbose = true); template<typename T> - T GetConfigValue(const std::string &var_name, const T &val, bool verbose = true) + T Value(const std::string &var_name, const T &val, bool verbose = true) { - return GetConfigValue(var_name, ConfigValue(val), verbose).Convert<T>(); + return Value(var_name, ConfigValue(val), verbose).Convert<T>(); } const std::string &GetConfigPath() const {return config_path;} const std::string &GetSplitChars() const {return split_chars;} const std::string &GetSpaceChars() const {return ignore_chars;} - const std::pair<std::string, std::string> &GetReplacePair() const {return replace_pair;} + const std::pair<std::string, std::string> &GetVariablePair() const {return variable_pair;} std::vector<std::string> GetKeyList() const; + const std::unordered_map<std::string, std::string> &GetMap() const {return config_map;} // functions that to be overloaded @@ -77,8 +79,8 @@ private: protected: std::string split_chars; std::string ignore_chars; + std::pair<std::string, std::string> variable_pair; bool case_insensitive; - std::pair<std::string, std::string> replace_pair; std::string config_path; std::unordered_map<std::string, std::string> config_map; diff --git a/third_party/prconf/include/ConfigParser.h b/third_party/prconf/include/ConfigParser.h index 5e2810b4b0ea14d3f0167fa05e9089c5542eed1d..65faeb2529d45adcc0ebcefad12ac19b96ad4025 100644 --- a/third_party/prconf/include/ConfigParser.h +++ b/third_party/prconf/include/ConfigParser.h @@ -3,10 +3,12 @@ #include <string> #include <vector> +#include <queue> #include <deque> #include <fstream> #include "ConfigValue.h" + // a macro to auto generate enum2str and str2enum // name mapping begins at bias and continuously increase, split by '|' // an example: @@ -32,79 +34,39 @@ class ConfigParser public: struct Format { + struct StrPair { std::string open, close; }; std::string split; // element splitters (chars) std::string white; // white spaces (chars) std::string delim; // line delimiter (string) std::string glue; // line glue (string) - std::string cmtmark; // comment marks (string), until the line breaker '\n' - std::string cmtopen; // comment-block opening mark (string) - std::string cmtclose; // comment-block closing mark (string) - - static Format Basic() {return {" \t,", " \t", "\n", "", "", "", ""};} - static Format BashLike() {return {" \t,", " \t", "\n", "\\", "#", "\'", "\'"};} - static Format CLike() {return {" \t,\n", " \t\n", ";", "", "//", "/*", "*/"};} - }; - - struct CharBuffer - { - std::vector<char> data; - size_t begin, end; - - CharBuffer(size_t cap = 256) : begin(0), end(0) - { - data.resize(cap); - } - - void Reset() {begin = 0; end = 0; data.clear();} - void Add(char ch) - { - if(data.size() <= end) - data.resize(2*data.size()); + std::string linecmt; // comment marks (string), until the line breaker '\n' + StrPair blockcmt; // block commenting marks (open string, close string) + StrPair quote; // quote marks (open string, close string) - data[end++] = ch; - } - - std::string String() - const - { - std::string str; - if(end > begin) - str.assign(&data[begin], end - begin); - return str; - } - - inline const char &operator [] (size_t idx) const {return data[idx];} - inline char &operator [] (size_t idx) {return data[idx];} + static Format Basic() { return {" \t,", " \t", "\n", "", "", {"", ""}, {"\"", "\""}}; } + static Format BashLike() { return {" \t,", " \t", "\n", "\\", "#", {"\'", "\'"}, {"\"", "\""}}; } + static Format CLike() { return {" \t,\n", " \t\n", ";", "", "//", {"/*", "*/"}, {"\"", "\""}}; } }; public: ConfigParser(Format f = Format::BashLike()); - ConfigParser(ConfigParser &&that); - ConfigParser(const ConfigParser &that); - - virtual ~ConfigParser(); - - ConfigParser &operator = (ConfigParser &&rhs); - ConfigParser &operator = (const ConfigParser &rhs); - // format related - inline void SetFormat(Format &&f) {form = f;} - inline void SetFormat(const Format &f) {form = f;} - inline void SetSplitters(std::string s) {form.split = s;} - inline void SetWhiteSpaces(std::string w) {form.white = w;} - inline void SetCommentMark(std::string c) {form.cmtmark = c;} - inline void SetCommentPair(std::string o, std::string c) {form.cmtopen = o; form.cmtclose = c;} - inline void SetLineGlues(std::string g) {form.glue = g;} - inline void SetLineBreaks(std::string b) {form.delim = b;} - - const Format &GetFormat() const {return form;} + inline void SetFormat(Format &&f) { fmt = f; } + inline void SetFormat(const Format &f) { fmt = f; } + inline void SetSplitters(std::string s) { fmt.split = s; } + inline void SetWhiteSpaces(std::string w) { fmt.white = w; } + inline void SetCommentMark(std::string c) { fmt.linecmt = c; } + inline void SetCommentPair(std::string o, std::string c) { fmt.blockcmt = {o, c}; } + inline void SetLineGlues(std::string g) { fmt.glue = g; } + inline void SetLineBreaks(std::string b) { fmt.delim = b; } + inline void SetQuotePair(std::string o, std::string c) { fmt.quote = {o, c}; } + + const Format &GetFormat() const {return fmt;} // dealing with file/buffer - bool OpenFile(const std::string &path, size_t cap = 64*1024); bool ReadFile(const std::string &path); void ReadBuffer(const char*); - void CloseFile(); void Clear(); // parse line, return false if no more line to parse @@ -116,9 +78,9 @@ public: // get current parsing status bool CheckElements(int num, int optional = 0); - int NbofElements() const {return elements.size();} - int LineNumber() const {return line_number;} - std::string CurrentLine() const {return cur_line.String();} + int NbofElements() const { return elements.size(); } + int LineNumber() const { return line_number; } + std::string CurrentLine() const; // take the elements ConfigValue TakeFirst(); @@ -179,22 +141,35 @@ public: private: // private functions - bool getBuffer(); - bool getLine(CharBuffer &line_buf, bool recursive = false); - int parseBuffer(const CharBuffer &line); + void toLines(std::string buf); + void parseBuffer(); + void retrieveLine(); private: // private members - Format form; - std::ifstream infile; - CharBuffer buf, cur_line; + Format fmt; int line_number; - std::deque<std::string> elements; + std::string curr_line; + std::vector<std::string> quotes; + std::deque<std::string> lines, elements; + public: // static functions static void comment_line(std::string &str, const std::string &cmt, const std::string &brk); + static void comment_line(std::string &str, const std::string &cmt, const std::string &brk, + const std::string &qmark); static void comment_between(std::string &str, const std::string &open, const std::string &close); + static void comment_between(std::string &str, const std::string &open, const std::string &close, + const std::string &qmark); + static void tokenize(std::string &str, std::vector<std::string> &contents, const std::string &token, + const std::string &open, const std::string &close); + static inline void tokenize(std::string &str, std::vector<std::string> &contents, const std::string &token, + const std::string &qmark) { tokenize(str, contents, token, qmark, qmark); } + static void untokenize(std::string &str, const std::vector<std::string> &contents, const std::string &token, + const std::string &open, const std::string &close); + static inline void untokenize(std::string &str, const std::vector<std::string> &contents, const std::string &token, + const std::string &qmark = "") { untokenize(str, contents, token, qmark, qmark); } static std::string trim(const std::string &str, const std::string &w); static std::deque<std::string> split(const std::string &str, const std::string &s); static std::deque<std::string> split(const char* str, const size_t &len, const std::string &s); diff --git a/third_party/prconf/include/ConfigValue.h b/third_party/prconf/include/ConfigValue.h index aaf3a36103cf96af200f2b68f1f330278a8ae46a..425912a7a56ca1800402f93fb8e0c1b0d22cebe3 100644 --- a/third_party/prconf/include/ConfigValue.h +++ b/third_party/prconf/include/ConfigValue.h @@ -82,6 +82,8 @@ public: const std::string &String() const {return _value;} bool IsEmpty() const {return _value.empty();} + ConfigValue &Trim(const std::string &white); + operator std::string() const { diff --git a/third_party/prconf/src/ConfigObject.cpp b/third_party/prconf/src/ConfigObject.cpp index ee18cd36afcc2b782b33a642e35618379256f7c6..33a98a898862f2bf6c3835427170dfa131efa5a3 100644 --- a/third_party/prconf/src/ConfigObject.cpp +++ b/third_party/prconf/src/ConfigObject.cpp @@ -9,6 +9,7 @@ // 10/31/2016 // //============================================================================// +#include <cstdlib> #include <fstream> #include <iostream> #include <iomanip> @@ -16,17 +17,17 @@ #include "ConfigObject.h" - //============================================================================// // Constructor, Destructor // //============================================================================// // constructor -ConfigObject::ConfigObject(const std::string &splitter, const std::string &ignore, bool case_ins) -: split_chars(splitter), ignore_chars(ignore), case_insensitive(case_ins), __empty_value("") +ConfigObject::ConfigObject(const std::string &splitter, const std::string &ignore, const std::string &var_open, + const std::string &var_close, bool case_ins) +: split_chars(splitter), ignore_chars(ignore), variable_pair({var_open, var_close}), case_insensitive(case_ins), + __empty_value("") { - // set default replace bracket - replace_pair = std::make_pair("{", "}"); + // place holder } // destructor @@ -79,36 +80,39 @@ bool ConfigObject::ReadConfigFile(const std::string &path) } // read the configuration string directly -void ConfigObject::ReadConfigString(const std::string &content) +void ConfigObject::ReadConfigString(const std::string &content, const std::string &path) { ConfigParser c_parser; c_parser.SetSplitters(split_chars); c_parser.ReadBuffer(content.c_str()); - parserProcess(c_parser, "buffer_string"); + parserProcess(c_parser, path); } // continue parse the terms void ConfigObject::parserProcess(ConfigParser &c_parser, const std::string &source) { - std::string cur_dir = ConfigParser::decompose_path(source).dir; + char abs_path[2048]; + realpath(source.c_str(), abs_path); + std::string cur_dir = ConfigParser::decompose_path(abs_path).dir; + + // THIS_DIR needs special treatment as many files in different dirs may be loaded into one instance + std::string dir_key = variable_pair.first + "THIS_DIR" + variable_pair.second; while (c_parser.ParseLine()) { // possible control words if (c_parser.NbofElements() == 1) { std::string control = c_parser.TakeFirst(); - size_t pos = control.find("{THIS_DIR}"); - if(pos != std::string::npos) - control.replace(pos, 10, cur_dir); + size_t pos = control.find(dir_key); + if(pos != std::string::npos) { control.replace(pos, dir_key.size(), cur_dir); } parseControl(control); // var_name and var_value } else if (c_parser.NbofElements() == 2) { std::string var_name, key, var_value; c_parser >> var_name >> var_value; - size_t pos = var_value.find("{THIS_DIR}"); - if(pos != std::string::npos) - var_value.replace(pos, 10, cur_dir); + size_t pos = var_value.find(dir_key); + if(pos != std::string::npos) { var_value.replace(pos, dir_key.size(), cur_dir); } parseTerm(std::move(var_name), std::move(var_value)); // unsupported format } else { @@ -174,7 +178,7 @@ const } // get configuration value by its name/key -ConfigValue ConfigObject::GetConfigValue(const std::string &var_name) +ConfigValue ConfigObject::Value(const std::string &var_name) const { // convert to lower case and remove uninterested characters @@ -185,7 +189,7 @@ const return __empty_value; } else { ConfigValue result(it->second); - reform(result._value, replace_pair.first, replace_pair.second); + reform(result._value, variable_pair.first, variable_pair.second); return result; } } @@ -202,7 +206,7 @@ void ConfigObject::SetConfigValue(const std::string &var_name, const ConfigValue // get configuration value from the map // if no such config value exists, it will fill the default value in -ConfigValue ConfigObject::GetConfigValue(const std::string &var_name, const ConfigValue &def_value, bool verbose) +ConfigValue ConfigObject::Value(const std::string &var_name, const ConfigValue &def_value, bool verbose) { auto key = formKey(var_name); @@ -222,7 +226,7 @@ ConfigValue ConfigObject::GetConfigValue(const std::string &var_name, const Conf } ConfigValue result(it->second); - reform(result._value, replace_pair.first, replace_pair.second); + reform(result._value, variable_pair.first, variable_pair.second); return result; } @@ -243,7 +247,7 @@ const return key; } -// replace the contents inside replace_pair with the configuration value +// replace the contents inside variable_pair with the configuration value void ConfigObject::reform(std::string &input, const std::string &op, const std::string &cl) const { @@ -258,17 +262,7 @@ const reform(var, op, cl); // replace content - std::string val; - - // environment variable - if (rpair.first > 0 && input.at(rpair.first - 1) == '$') { - val = std::getenv(var.c_str()); - // replace $ mark also - rpair.first--; - // ConfigObject variable - } else { - val = GetConfigValue(var)._value; - } + std::string val = HasKey(var) ? Value(var)._value : std::getenv(var.c_str()); // replace variable with configuration value input.replace(rpair.first, rpair.second - rpair.first + cl.size(), val); @@ -304,7 +298,7 @@ void ConfigObject::parseControl(const std::string &word) int length = p.second - begin; std::string new_path = word.substr(begin, length); - reform(new_path, replace_pair.first, replace_pair.second); + reform(new_path, variable_pair.first, variable_pair.second); ReadConfigFile(new_path); } diff --git a/third_party/prconf/src/ConfigParser.cpp b/third_party/prconf/src/ConfigParser.cpp index ad496d48e13bfeacdfda01dd92178482dd778c69..36e63537ace3fee0ce063451926478ea28077542 100644 --- a/third_party/prconf/src/ConfigParser.cpp +++ b/third_party/prconf/src/ConfigParser.cpp @@ -12,6 +12,10 @@ using namespace std; +#define TOKEN_DIGITS 5 + +// TODO radmonize token that has no conflic with the format marks +static const string config_token = "Gc2xConfig6R"; //============================================================================// @@ -20,114 +24,53 @@ using namespace std; // constructor, with format input ConfigParser::ConfigParser(Format f) -: form(f), line_number(0) -{ - // place holder -} - -// copy constructor, only copy format -ConfigParser::ConfigParser(const ConfigParser &that) -: form(that.form), line_number(0) +: fmt(f), line_number(0) { // place holder } -// move constructor, only move format -ConfigParser::ConfigParser(ConfigParser &&that) -: form(that.form), line_number(0) -{ - // place holder -} - -// desctructor -ConfigParser::~ConfigParser() -{ - CloseFile(); -} - -// copy assignment operator -ConfigParser &ConfigParser::operator = (const ConfigParser &rhs) -{ - form = rhs.form; - return *this; -} - -// move assignment operator -ConfigParser &ConfigParser::operator = (ConfigParser &&rhs) -{ - form = rhs.form; - return *this; -} - //============================================================================// // Public Member Function // //============================================================================// -// open a file for future parsing -bool ConfigParser::OpenFile(const string &path, size_t cap) -{ - Clear(); - - infile.open(path); - - if(!infile.is_open()) - return false; - - buf.data.resize(cap); - - // success! - return true; -} - // read the whole file into a buffer and break it into lines bool ConfigParser::ReadFile(const string &path) { - Clear(); - - infile.open(path); + ifstream inf(path); - if(!infile.is_open()) + if(!inf.is_open()) return false; - infile.seekg(0, ios::end); - buf.end = infile.tellg(); - buf.data.resize(buf.end); - infile.seekg(0, ios::beg); + // read the whole file in + string str; + + inf.seekg(0, ios::end); + str.reserve(inf.tellg()); + inf.seekg(0, ios::beg); - infile.read(&buf.data[0], buf.end); - infile.close(); + str.assign((istreambuf_iterator<char>(inf)), istreambuf_iterator<char>()); + inf.close(); + + toLines(str); return true; } -// close file -void ConfigParser::CloseFile() +// read a buffer in +void ConfigParser::ReadBuffer(const char *buf_in) { - return infile.close(); + toLines(buf_in); } // clear stored lines void ConfigParser::Clear() { - buf.Reset(); + lines.clear(); + elements.clear(); // reset line line_number = 0; - cur_line.Reset(); - - // close file - CloseFile(); -} - -// read a buffer in -void ConfigParser::ReadBuffer(const char *buf_in) -{ - Clear(); - - buf.end = strlen(buf_in); - buf.data.resize(buf.end + 2); - - strncpy(&buf.data[0], buf_in, buf.end); + curr_line.clear(); } // parse a line from the file or buffer @@ -135,57 +78,38 @@ void ConfigParser::ReadBuffer(const char *buf_in) // return false if reached the end bool ConfigParser::ParseLine() { - elements.clear(); - - while(elements.empty()) - { - if(!getLine(cur_line)) - return false; - - // count the line number - ++line_number; - - parseBuffer(cur_line); + if (lines.empty()) { + return false; } - return true; + elements.clear(); + parseBuffer(); + + return static_cast<bool>(elements.size()); } + // parse the whole file or buffer // return false if nothing was found bool ConfigParser::ParseAll() { elements.clear(); - while(true) - { - if(!getLine(cur_line)) - return !elements.empty(); - - // count the line number - ++line_number; - - parseBuffer(cur_line); + while (lines.size()) { + parseBuffer(); } + + return static_cast<bool>(elements.size()); } // parse an input string, split the string into elements // the trail white spaces in the elements will be trimmed int ConfigParser::ParseString(const string &line) { - deque<string> eles = split(line.c_str(), line.size(), form.split); + toLines(line); + ParseAll(); - int count = 0; - for(auto &ele : eles) - { - string trim_ele = trim(ele, form.white); - if(!trim_ele.empty()) { - elements.emplace_back(move(trim_ele)); - count++; - } - } - - return count; + return elements.size(); } // check if the current elemtns number is in the range [num, num + optional] @@ -223,12 +147,20 @@ bool ConfigParser::CheckElements(int num, int optional) << line_number << ", expecting " << num_str << " elements. " << endl - << "\"" << cur_line.String() << "\"" + << "\"" << CurrentLine() << "\"" << endl; return false; } +// get current line +string ConfigParser::CurrentLine() const +{ + string res = curr_line; + untokenize(res, quotes, config_token, fmt.quote.open, fmt.quote.close); + return res; +} + // take the first element ConfigValue ConfigParser::TakeFirst() { @@ -249,165 +181,102 @@ ConfigValue ConfigParser::TakeFirst() //============================================================================// // Private Member Function // //============================================================================// - -// get buffer from the file or the input buffer -// return false if reached input end -bool ConfigParser::getBuffer() +// helper function +inline bool compare_str(const char *buf1, const char *buf2, size_t n) { - if(buf.begin < buf.end) - return true; - - if(!infile.is_open() || infile.bad() || infile.eof()) - return false; - - infile.read(&buf.data[0], buf.data.size()); - - buf.begin = 0; - buf.end = infile.gcount(); - + for (size_t i = 0; i < n; ++i) { + if (buf1[i] != buf2[i]) { + return false; + } + } return true; } -// trim white spaces -inline void trimbuf(const vector<char> &buf, size_t &begin, size_t &end, const string &w) +// helper function +inline string break_line(const string &buf, const std::string &delim, size_t &i, const std::string &glue) { - while(begin < end) - { - if(w.find(buf[begin]) == string::npos) - break; - - begin++; + size_t beg = i; + if (delim.empty()) { + i = buf.size(); + return buf.substr(beg); } - while(end > begin) - { - if(w.find(buf[end - 1]) == string::npos) - break; - - end--; + for (; i <= buf.size() - delim.size(); ++i) { + if (compare_str(&buf[i], delim.c_str(), delim.size())) { + string res = buf.substr(beg, i - beg); + i += delim.size(); + if (glue.size() && (res.size() > glue.size())) { + size_t pos = res.size() - glue.size(); + if (compare_str(&res[pos], glue.c_str(), glue.size())) { + return res.substr(0, pos) + break_line(buf, delim, i, glue); + } + } + return res; + } } + i = buf.size(); + return buf.substr(beg); } -inline bool compare(const char ch, const string &str, size_t &c1) +// parse the buffer string, remove comments, tokenize quotes, and split it in lines +void ConfigParser::toLines(string buf) { - if(str.empty()) - return false; + Clear(); - c1 = (ch == str[c1]) ? (c1 + 1) : 0; + if (buf.empty()) { + return; + } - return (c1 >= str.size()); + // break into lines + size_t ibuf = 0; + while (ibuf < buf.size()) { + lines.emplace_back(break_line(buf, fmt.delim, ibuf, fmt.glue)); + } } -inline bool rcompare(const ConfigParser::CharBuffer &buf, const string &str) +void ConfigParser::retrieveLine() { - if(str.empty() || buf.end <= buf.begin || buf.end - buf.begin < str.size()) - return false; + curr_line += move(lines.front()); + lines.pop_front(); + tokenize(curr_line, quotes, config_token, fmt.quote.open, fmt.quote.close); + comment_between(curr_line, fmt.blockcmt.open, fmt.blockcmt.close); + comment_line(curr_line, fmt.linecmt, fmt.delim); - for(size_t i = 1; i <= str.size(); ++i) - { - if(str[str.size() - i] != buf[buf.end - i]) - return false; + if (lines.empty()) { return; } + if (curr_line.empty() || (curr_line.find(fmt.blockcmt.open) != string::npos)) { + retrieveLine(); } - - return true; } -// a helper structure to check context status -struct TextStatus +// parse buffered lines +void ConfigParser::parseBuffer() { - int val; - size_t cmt1, cmt2, delim; + size_t count = 0; + curr_line.clear(); + quotes.clear(); - TextStatus() : val(0), cmt1(0), cmt2(0), delim(0) {} - inline void Set(int i) {val = i; cmt1 = 0; cmt2 = 0;} -}; + while (!count && lines.size()) + { + retrieveLine(); -// get a line from the file or buffer -// it deals with comments, white spaces -// return false if reached the end -bool ConfigParser::getLine(CharBuffer &line_buf, bool recursive) -{ - if(!recursive) - line_buf.Reset(); - bool success = false; - TextStatus stat; + trim(curr_line, fmt.white); + auto eles = split(curr_line, fmt.split); - while(getBuffer()) - { - success = true; - - while(buf.begin < buf.end) - { - auto &ch = buf[buf.begin++]; - switch(stat.val) - { - default: - case 0: - line_buf.Add(ch); - // check if it is the end - if(compare(ch, form.delim, stat.delim)) { - line_buf.end -= form.delim.size(); - trimbuf(line_buf.data, line_buf.begin, line_buf.end, form.white); - // glue lines - if(rcompare(line_buf, form.glue)) { - line_buf.end -= form.glue.size(); - return getLine(line_buf, true); - } else { - return success; - } - } else if(compare(ch, form.cmtopen, stat.cmt1)) { - stat.Set(1); - line_buf.end -= form.cmtopen.size(); - } else if(compare(ch, form.cmtmark, stat.cmt2)) { - stat.Set(2); - line_buf.end -= form.cmtmark.size(); - } - break; - case 1: - if(compare(ch, form.cmtclose, stat.cmt1)) { - stat.Set(0); - } - break; - case 2: - if(ch == '\n') { - stat.Set(0); - buf.begin -= 1; - } - break; + // trim every element and replace token with info + for (auto &ele : eles) { + ele = trim(ele, fmt.white); + if (ele.empty()) { + continue; } + untokenize(ele, quotes, config_token); + elements.emplace_back(move(ele)); + count++; } + line_number++; } - - trimbuf(line_buf.data, line_buf.begin, line_buf.end, form.white); - return success; } -// parse an input char buffer, split the string into elements -// the trail white spaces in the elements will be trimmed -int ConfigParser::parseBuffer(const CharBuffer &line) -{ - if(line.begin >= line.end) - return 0; - size_t ele_begin = line.begin; - int count = 0; - - // intended to visit i == line.end, so the rest of the string get parsed - for(size_t i = 0; i <= line.end; ++i) - { - if(i == line.end || form.split.find(line[i]) != string::npos) { - size_t ele_end = i; - trimbuf(line.data, ele_begin, ele_end, form.white); - if(ele_begin < ele_end) { - elements.emplace_back(&line[ele_begin], ele_end - ele_begin); - count++; - } - ele_begin = i + 1; - } - } - - return count; -} //============================================================================// // Public Static Function // @@ -443,22 +312,77 @@ void ConfigParser::comment_line(string &str, const string &c, const string &b) } } +// comment out a string, consider quotes +void ConfigParser::comment_line(string &str, const string &c, const string &b, const string &qmark) +{ + vector<string> quotes; + tokenize(str, quotes, config_token, qmark, qmark); + comment_line(str, c, b); + untokenize(str, quotes, config_token, qmark, qmark); +} + +// tokenize the content between quote marks, no nested structure supported +void ConfigParser::tokenize(string &str, vector<string> &contents, const string &token, + const string &open, const string &close) +{ + if (str.empty() || open.empty() || close.empty()) + return; + + string padzero(TOKEN_DIGITS, '0'); + while (true) { + size_t pos1 = str.find(open); + if (pos1 != string::npos) { + size_t pos2 = str.find(close, pos1 + open.size()); + // found pair + if (pos2 != string::npos) { + string digits = (padzero + to_string(contents.size())); + contents.emplace_back(str.substr(pos1 + open.size(), pos2 - pos1 - open.size())); + str.replace(pos1, pos2 + close.size() - pos1, token + digits.substr(digits.size() - TOKEN_DIGITS)); + } else { + return; + } + } else { + return; + } + } +} + +// reversal of tokenize +void ConfigParser::untokenize(string &str, const vector<string> &contents, const string &token, + const string &open, const string &close) +{ + if (contents.empty() || str.empty() || token.empty()) { + return; + } + + auto pos = str.find(token); + auto size = token.size() + TOKEN_DIGITS; + while ((pos != string::npos) && ((pos + size) <= str.size())) { + size_t id = stoul(str.substr(pos + token.size(), TOKEN_DIGITS)); + if (open.empty() || close.empty()) { + str.replace(pos, size, contents[id]); + } else { + str.replace(pos, size, open + contents[id] + close); + } + pos = str.find(token); + } +} + // comment out between a pair of comment marks // NOTICE: does not support nested structure of comment marks void ConfigParser::comment_between(string &str, const string &open, const string &close) { // no need to continue - if(str.empty() || open.empty() || close.empty()) + if (str.empty() || open.empty() || close.empty()) return; - while(true) - { + while (true) { // find the openning comment mark size_t pos1 = str.find(open); - if(pos1 != string::npos) { + if (pos1 != string::npos) { size_t pos2 = str.find(close, pos1 + open.size()); // found pair - if(pos2 != string::npos) { + if (pos2 != string::npos) { // remove everything between, including this pair str.erase(pos1, pos2 + close.size() - pos1); // comment pair not found @@ -472,6 +396,15 @@ void ConfigParser::comment_between(string &str, const string &open, const string } } +// comment out a string, consider quotes +void ConfigParser::comment_between(string &str, const string &open, const string &close, const string &qmark) +{ + vector<string> quotes; + tokenize(str, quotes, config_token, qmark, qmark); + comment_between(str, open, close); + untokenize(str, quotes, config_token, qmark, qmark); +} + // trim all the characters defined as white space at both ends string ConfigParser::trim(const string &str, const string &w) { diff --git a/third_party/prconf/src/ConfigValue.cpp b/third_party/prconf/src/ConfigValue.cpp index 96bd4b2ead9db0fe957b8cb11cb1c260f4e75fae..632d88f558943474e49afd5d211f7ed013c67ffb 100644 --- a/third_party/prconf/src/ConfigValue.cpp +++ b/third_party/prconf/src/ConfigValue.cpp @@ -305,6 +305,19 @@ const return _value.c_str(); } +ConfigValue &ConfigValue::Trim(const std::string &white) +{ + const auto strBegin = _value.find_first_not_of(white); + if (strBegin == string::npos) { + _value = ""; + } else { + const auto strEnd = _value.find_last_not_of(white); + const auto strRange = strEnd - strBegin + 1; + _value = _value.substr(strBegin, strRange); + } + + return *this; +} //============================================================================//