|
| 1 | +/* |
| 2 | + * | |
| 3 | + * __| _ \ _ \ | _` | | | |
| 4 | + * | __/ __/ | ( | | | |
| 5 | + * _| \___|\___|_|\__,_|\__, | |
| 6 | + * ____/ Copyright (c) 2019-2020 Dogan Ulus |
| 7 | + * |
| 8 | + * This Source Code Form is subject to the terms of the Mozilla Public |
| 9 | + * License, v. 2.0. If a copy of the MPL was not distributed with this |
| 10 | + * file, You can obtain one at http://mozilla.org/MPL/2.0/. |
| 11 | + */ |
| 12 | +#include <glob.h> // glob(), globfree() |
| 13 | +#include <string.h> // memset() |
| 14 | + |
| 15 | +#include <fstream> |
| 16 | +#include <iostream> |
| 17 | +#include <memory> |
| 18 | +#include <stdexcept> |
| 19 | +#include <string> |
| 20 | +#include <vector> |
| 21 | + |
| 22 | +#include "reelay/json.hpp" |
| 23 | +#include "reelay/monitors.hpp" |
| 24 | + |
| 25 | +#include "third_party/simdjson/simdjson.h" |
| 26 | +#include "third_party/simdjson/simdjson.cpp" |
| 27 | +#include "third_party/taywee/args.hpp" |
| 28 | + |
| 29 | +#include "simdjson_adapter.hpp" |
| 30 | + |
| 31 | +namespace rycli { |
| 32 | + |
| 33 | +std::vector<std::string> expand_glob(const std::string& glob_str) { |
| 34 | + |
| 35 | + // glob struct resides on the stack |
| 36 | + glob_t glob_result; |
| 37 | + memset(&glob_result, 0, sizeof(glob_result)); |
| 38 | + |
| 39 | + // do the glob operation |
| 40 | + int return_value |
| 41 | + = glob(glob_str.c_str(), GLOB_TILDE, NULL, &glob_result); |
| 42 | + |
| 43 | + // glob() error handling |
| 44 | + // Info: http://man7.org/linux/man-pages/man3/glob.3.html |
| 45 | + if (return_value == GLOB_ABORTED) { |
| 46 | + globfree(&glob_result); |
| 47 | + std::stringstream ss; |
| 48 | + ss << "glob() encountered a read error" << std::endl; |
| 49 | + throw std::runtime_error(ss.str()); |
| 50 | + } else if (return_value == GLOB_NOSPACE) { |
| 51 | + globfree(&glob_result); |
| 52 | + std::stringstream ss; |
| 53 | + ss << "glob() running out of memory" << std::endl; |
| 54 | + throw std::runtime_error(ss.str()); |
| 55 | + } |
| 56 | + |
| 57 | + // collect all the filenames into a std::list<std::string> |
| 58 | + std::vector<std::string> filenames; |
| 59 | + for (size_t i = 0; i < glob_result.gl_pathc; ++i) { |
| 60 | + filenames.push_back(std::string(glob_result.gl_pathv[i])); |
| 61 | + } |
| 62 | + |
| 63 | + // cleanup |
| 64 | + globfree(&glob_result); |
| 65 | + |
| 66 | + // done |
| 67 | + return filenames; |
| 68 | +} |
| 69 | + |
| 70 | +template <class MonitorT> |
| 71 | +inline void discrete_timed_process_json_file(MonitorT monitor, const std::string& filename) { |
| 72 | + int stdout_line_count = 0; |
| 73 | + simdjson::dom::parser reader; |
| 74 | + std::ofstream output_file(filename + ".ryl"); |
| 75 | + std::cout << "Processing " + filename << std::endl; |
| 76 | + std::cout << "---" << std::endl; |
| 77 | + for (simdjson::dom::element doc : reader.load_many(filename)) { |
| 78 | + reelay::json result = monitor->update(doc); |
| 79 | + if (not result.empty()) { |
| 80 | + if (stdout_line_count < 5) { |
| 81 | + std::cout << result << std::endl; |
| 82 | + stdout_line_count++; |
| 83 | + } else if (stdout_line_count == 5) { |
| 84 | + std::cout << "..." << std::endl; |
| 85 | + stdout_line_count++; |
| 86 | + } |
| 87 | + output_file << result << std::endl; |
| 88 | + }; |
| 89 | + } |
| 90 | + if (stdout_line_count < 5){ |
| 91 | + std::cout << "---" << std::endl; |
| 92 | + } |
| 93 | + std::cout << "Full output written to " + filename + ".ryl" << std::endl; |
| 94 | +} |
| 95 | + |
| 96 | +template <class MonitorT> |
| 97 | +inline void dense_timed_process_json_file(MonitorT monitor, const std::string& filename) { |
| 98 | + int stdout_line_count = 0; |
| 99 | + simdjson::dom::parser reader; |
| 100 | + std::ofstream output_file(filename + ".ryl"); |
| 101 | + std::cout << "Processing " + filename << std::endl; |
| 102 | + std::cout << "---" << std::endl; |
| 103 | + for (simdjson::dom::element doc : reader.load_many(filename)) { |
| 104 | + reelay::json result = monitor->update(doc); |
| 105 | + if (not result.empty()) { |
| 106 | + for(const auto& item: result){ |
| 107 | + if (stdout_line_count < 5) { |
| 108 | + std::cout << item << std::endl; |
| 109 | + stdout_line_count++; |
| 110 | + } else if (stdout_line_count == 5) { |
| 111 | + std::cout << "..." << std::endl; |
| 112 | + stdout_line_count++; |
| 113 | + } |
| 114 | + output_file << item << std::endl; |
| 115 | + } |
| 116 | + } |
| 117 | + } |
| 118 | + if (stdout_line_count < 5) { |
| 119 | + std::cout << "---" << std::endl; |
| 120 | + } |
| 121 | + std::cout << "Full output written to " + filename + ".ryl" << std::endl; |
| 122 | +} |
| 123 | + |
| 124 | +} // namespace rycli |
| 125 | + |
| 126 | +int main(int argc, char** argv) { |
| 127 | + |
| 128 | + using namespace reelay; |
| 129 | + |
| 130 | + args::ArgumentParser parser( |
| 131 | + "Reelay Command Line Interface", |
| 132 | + "Further information: https://github.com/doganulus/reelay"); |
| 133 | + |
| 134 | + args::Positional<std::string> spec( |
| 135 | + parser, "SPEC", "Specification in Rye Format", args::Options::Required); |
| 136 | + args::PositionalList<std::string> paths( |
| 137 | + parser, "FILE", "Log files to be checked against SPEC", |
| 138 | + args::Options::Required); |
| 139 | + |
| 140 | + args::Group gsetting(parser, "Monitor Settings"); |
| 141 | + |
| 142 | + args::Group gmodel(gsetting, "Time model:", args::Group::Validators::Xor, |
| 143 | + args::Options::Global); |
| 144 | + args::Flag fdense(gmodel, "fv", "Use dense time model (default)", |
| 145 | + {'v', "dense"}); |
| 146 | + args::Flag fdiscrete(gmodel, "fx", "Use discrete time model", |
| 147 | + {'x', "discrete"}); |
| 148 | + |
| 149 | + args::Group gdtype(gsetting, "Time datatype:", args::Group::Validators::Xor, |
| 150 | + args::Options::Global); |
| 151 | + args::Flag fint(gdtype, "fi", "Use int64 as time type (default)", |
| 152 | + {'i', "itime"}); |
| 153 | + args::Flag ffloat(gdtype, "ff", "with -v, use float64 as time type", |
| 154 | + {'f', "ftime"}); |
| 155 | + |
| 156 | + args::Group gsemantics(gsetting, "Value model:", args::Group::Validators::Xor, |
| 157 | + args::Options::Global); |
| 158 | + args::Flag fboolean( |
| 159 | + gsemantics, "fb", "Use boolean semantics", {'b', "boolean"}); |
| 160 | + args::Flag frobustness( |
| 161 | + gsemantics, "fr", "Use robustness semantics", {'r', "robustness"}); |
| 162 | + |
| 163 | + args::Group ginterp(gsetting, "Interpolation:", args::Group::Validators::Xor, |
| 164 | + args::Options::Global); |
| 165 | + args::Flag fconstant(ginterp, "fk", |
| 166 | + "with -v, use piecewise constant interpolation (default)", |
| 167 | + {'k', "pwc"}); |
| 168 | + args::Flag flinear(ginterp, "fl", "with -vf, use piecewise linear interpolation", |
| 169 | + {'l', "pwl"}); |
| 170 | + |
| 171 | + args::ValueFlag<double> fperiod( |
| 172 | + gsetting, "NUM", "with -v, use NUM as the sampling period", |
| 173 | + {"period"}); |
| 174 | + args::Flag fno_condense(gsetting, "fno-condense", |
| 175 | + "with -x, disable dense output", {'z', "no-condense"}); |
| 176 | + |
| 177 | + args::Group gioctrl(parser, "Input/Output Control"); |
| 178 | + args::Group gformat(gioctrl, |
| 179 | + "Input File Format:", args::Group::Validators::Xor, |
| 180 | + args::Options::Global); |
| 181 | + |
| 182 | + args::ValueFlag<std::string> t_name( |
| 183 | + gioctrl, "STRING", "Use STRING as the name of time field", {"tname"}, "time"); |
| 184 | + args::ValueFlag<std::string> y_name( |
| 185 | + gioctrl, "STRING", "use STRING as the name of output field", {"yname"}, "value"); |
| 186 | + |
| 187 | + args::HelpFlag help(parser, "help", "Display this help menu", {'h', "help"}); |
| 188 | + |
| 189 | + args::CompletionFlag completion(parser, {"complete"}); |
| 190 | + |
| 191 | + try { |
| 192 | + parser.ParseCLI(argc, argv); |
| 193 | + } catch (const args::Completion& e) { |
| 194 | + std::cout << e.what(); |
| 195 | + return 0; |
| 196 | + } catch (const args::Help&) { |
| 197 | + std::cout << parser; |
| 198 | + return 0; |
| 199 | + } catch (const args::ParseError& e) { |
| 200 | + std::cerr << e.what() << std::endl; |
| 201 | + std::cerr << parser; |
| 202 | + return 1; |
| 203 | + } |
| 204 | + |
| 205 | + // Glob Expansion |
| 206 | + auto filenames = std::vector<std::string>(); |
| 207 | + for (const auto& p : args::get(paths)) { |
| 208 | + std::vector<std::string> expanded = rycli::expand_glob(p); |
| 209 | + filenames.insert(filenames.end(), expanded.begin(), expanded.end()); |
| 210 | + } |
| 211 | + |
| 212 | + using input_t = simdjson::dom::element; |
| 213 | + |
| 214 | + reelay::kwargs extra_args |
| 215 | + = {{"t_name", args::get(t_name)}, {"y_name", args::get(y_name)}}; |
| 216 | + |
| 217 | + if (fdiscrete) { |
| 218 | + std::shared_ptr<base_monitor<int64_t, input_t, json>> m; |
| 219 | + |
| 220 | + if (frobustness and fno_condense) { |
| 221 | + m = discrete_timed<int64_t>::robustness<double>::monitor<input_t>::make( |
| 222 | + args::get(spec), extra_args); |
| 223 | + } else if (frobustness and not fno_condense) { |
| 224 | + m = condensing<int64_t>::robustness<double>::monitor<input_t>::make( |
| 225 | + args::get(spec), extra_args); |
| 226 | + } else if (not frobustness and fno_condense) { |
| 227 | + m = discrete_timed<int64_t>::monitor<input_t>::make( |
| 228 | + args::get(spec), extra_args); |
| 229 | + } else { |
| 230 | + m = condensing<int64_t>::monitor<input_t>::make( |
| 231 | + args::get(spec), extra_args); |
| 232 | + } |
| 233 | + |
| 234 | + for (const auto& filename : filenames) { |
| 235 | + rycli::discrete_timed_process_json_file<decltype(m)>(m, filename); |
| 236 | + } |
| 237 | + } else if (ffloat) { // fdense and ffloat |
| 238 | + std::shared_ptr<base_monitor<double, input_t, std::vector<json>>> m; |
| 239 | + |
| 240 | + if (frobustness and flinear) { |
| 241 | + throw std::invalid_argument( |
| 242 | + ("Linear interpolation under robustness semantics not supported")); |
| 243 | + } else if (frobustness and not flinear) { |
| 244 | + m = reelay::dense_timed<double, piecewise::CONSTANT>::robustness< |
| 245 | + double>::monitor<input_t>::make(args::get(spec), extra_args); |
| 246 | + } else if (not frobustness and flinear) { |
| 247 | + m = reelay::dense_timed<double, piecewise::LINEAR>::monitor< |
| 248 | + input_t>::make(args::get(spec), extra_args); |
| 249 | + } else { // not frobustness and not flinear |
| 250 | + m = reelay::dense_timed<double, piecewise::CONSTANT>::monitor< |
| 251 | + input_t>::make(args::get(spec), extra_args); |
| 252 | + } |
| 253 | + |
| 254 | + for (const auto& filename : filenames) { |
| 255 | + rycli::dense_timed_process_json_file<decltype(m)>(m, filename); |
| 256 | + } |
| 257 | + } else { // fdense and fint |
| 258 | + std::shared_ptr<base_monitor<int64_t, input_t, std::vector<json>>> m; |
| 259 | + |
| 260 | + if (frobustness and flinear) { |
| 261 | + throw std::invalid_argument(( |
| 262 | + "Integer time type and linear interpolation does not work together")); |
| 263 | + } else if (frobustness and not flinear) { |
| 264 | + m = reelay::dense_timed<int64_t, piecewise::CONSTANT>::robustness< |
| 265 | + double>::monitor<input_t>::make(args::get(spec), extra_args); |
| 266 | + } else if (not frobustness and flinear) { |
| 267 | + throw std::invalid_argument(( |
| 268 | + "Integer time type and linear interpolation does not work together")); |
| 269 | + } else { // not frobustness and not flinear |
| 270 | + m = reelay::dense_timed<int64_t, piecewise::CONSTANT>::monitor< |
| 271 | + input_t>::make(args::get(spec), extra_args); |
| 272 | + } |
| 273 | + |
| 274 | + for (const auto& filename : filenames) { |
| 275 | + rycli::dense_timed_process_json_file<decltype(m)>(m, filename); |
| 276 | + } |
| 277 | + } |
| 278 | + |
| 279 | + return 0; |
| 280 | +} |
0 commit comments