#include "trygvis/sensor.h" #include "trygvis/sensor/io.h" #include "apps.h" #include #include namespace trygvis { namespace apps { using namespace std; using namespace trygvis::apps; using namespace trygvis::sensor; using namespace trygvis::sensor::io; namespace po = boost::program_options; enum class time_resolution { SECONDS, MILLISECONDS, }; std::ostream& operator<<(std::ostream& os, time_resolution const& type) { if (type == time_resolution::SECONDS) { os << "seconds"; } else if(type == time_resolution::MILLISECONDS) { os << "milliseconds"; } return os; } std::istream& operator>>(std::istream& is, time_resolution & type) { string s; is >> s; if(s == "seconds") { type = time_resolution::SECONDS; } else if(s == "milliseconds") { type = time_resolution::MILLISECONDS; } return is; } class TimestampFixingSampleOutputStream : public SampleOutputStream { public: TimestampFixingSampleOutputStream(shared_ptr output, KeyDictionary &dict, string timestamp_name, string relative_name, time_resolution input_time_resolution, time_t start_time) : timestamp_key(dict.indexOf(timestamp_name)), relative_key(dict.indexOf(relative_name)), input_time_resolution_(input_time_resolution), start_time_(start_time), output_(output) { } virtual void write(SampleRecord const &sample) override { o relative_time_o = sample.lexical_at(relative_key); if (!relative_time_o) { return; } long relative_time = relative_time_o.get(); long updated_time; if (input_time_resolution_ == time_resolution::SECONDS) { updated_time = start_time_ + relative_time; } else if (input_time_resolution_ == time_resolution::MILLISECONDS) { updated_time = start_time_ + relative_time / 1000; } string updated_value = std::to_string(updated_time); SampleRecord updated_sample(sample); updated_sample.set(timestamp_key, updated_value); output_->write(updated_sample); }; private: const SampleKey* relative_key, *timestamp_key; time_t start_time_; time_resolution input_time_resolution_; shared_ptr output_; }; class sample_timestamp : public app { private: string input_file, timestamp_name, relative_name; time_resolution relative_resolution; SampleKey* relative_key; public: sample_timestamp() : input_file("") { } void add_options(po::options_description_easy_init &options) override { options ("help", "produce this help message") ("input", po::value(&input_file)->required()) ("relative-name", po::value(&relative_name)->default_value("relative")) ("relative-resolution", po::value(&relative_resolution)->default_value(time_resolution::SECONDS)) ("timestamp-name", po::value(×tamp_name)->default_value("timestamp")); } int main(app_execution &execution) override { ifstream input(input_file, std::ifstream::in | std::ifstream::binary); if (input.fail()) { cerr << "Could not open file: " << input_file << endl; return EXIT_FAILURE; } const int buffer_size = 1024; input.seekg(-buffer_size, ios_base::end); struct stat buf; if (stat(input_file.c_str(), &buf)) { cerr << "stat failed" << endl; return EXIT_FAILURE; } KeyDictionary dict; relative_key = dict.indexOf(relative_name); auto sample_buffer = make_shared(); auto parser = open_sample_stream_parser(sample_buffer, dict); while (!input.fail()) { char buffer[buffer_size]; input.read(buffer, buffer_size); auto count = (size_t) input.gcount(); cerr << "eof? " << input.eof() << endl; mutable_buffers_1 b = boost::asio::buffer(buffer, count); parser->process(b); } if (sample_buffer->samples.empty()) { cerr << "Could not find any samples" << endl; return EXIT_FAILURE; } time_t end_time = buf.st_mtim.tv_sec; SampleRecord sample = *--sample_buffer->samples.end(); o s = sample.at(relative_key); if (!s) { cerr << "Missing key '" + relative_name + "'." << endl; cerr << "Found " << sample_buffer->samples.size() << " samples." << endl; cerr << "keys: " << sample.to_string() << endl; return EXIT_FAILURE; } long relative; try { relative = boost::lexical_cast(s.get()); } catch (const boost::bad_lexical_cast &e) { cerr << "Bad integer value '" + s.get() + "'." << endl; return EXIT_FAILURE; } if (relative_resolution == time_resolution::MILLISECONDS) { relative /= 1000; } time_t start_time = end_time - relative; cerr << "end_time " << end_time << endl; cerr << "relative " << relative << endl; cerr << "start_time " << start_time << endl; // Restart the reading of the input file and add the adjusted timestamp input.clear(ios::eofbit); input.seekg(0); if(input.fail()) { cerr << "Coult not seek input file" << endl; return EXIT_FAILURE; } vector options = {}; unique_ptr unique_output_stream = open_sample_output_stream(shared_ptr(&cout, noop_deleter), dict, parser->type(), options); shared_ptr output_stream{std::move(unique_output_stream)}; shared_ptr p = make_shared(output_stream, dict, timestamp_name, relative_name, relative_resolution, start_time); parser = open_sample_stream_parser(p, dict, parser->type()); int recordCount = 0; while (!input.eof()) { char buffer[buffer_size]; input.read(buffer, buffer_size); size_t gcount = (size_t)input.gcount(); recordCount++; mutable_buffers_1 b = boost::asio::buffer(buffer, gcount); parser->process(b); } return EXIT_SUCCESS; }; }; } } using namespace trygvis::apps; int main(int argc, char *argv[]) { sample_timestamp app; return launch_app(argc, argv, app); }