diff options
-rw-r--r-- | apps/SoilMoistureIo.cpp | 140 | ||||
-rw-r--r-- | apps/SoilMoistureIo.h | 26 | ||||
-rw-r--r-- | apps/sample-convert.cpp | 105 |
3 files changed, 145 insertions, 126 deletions
diff --git a/apps/SoilMoistureIo.cpp b/apps/SoilMoistureIo.cpp index 98a256b..24618ff 100644 --- a/apps/SoilMoistureIo.cpp +++ b/apps/SoilMoistureIo.cpp @@ -11,94 +11,140 @@ namespace soil_moisture { using namespace std; using json = nlohmann::json; +CsvSampleOutputStream::CsvSampleOutputStream(ostream &stream) : + stream(stream), filterFields(false), headerWritten(false) { +} + CsvSampleOutputStream::CsvSampleOutputStream(ostream &stream, vector<string> fields) : - stream(stream), fields(fields) { + stream(stream), fields(fields), filterFields(true), headerWritten(false) { +} - auto i = fields.begin(); - while (true) { - stream << *i; +void CsvSampleOutputStream::write(Sample values) { + if (!headerWritten) { + writeHeader(); + headerWritten = true; + } - i++; + if (filterFields) { + auto i = fields.begin(); + while (i != fields.end()) { + if (i != fields.begin()) { + stream << ","; + } - if (i != fields.end()) { - stream << ","; - } else { - break; + auto field = *i++; + auto value = values.find(field); + + if (value != values.end()) { + stream << value->second; + } + } + } else { + for (auto i = values.begin(); i != values.end();) { + stream << "\"" << (*i).second << "\""; + + if (++i != values.end()) { + stream << ","; + } } } stream << endl; } -void CsvSampleOutputStream::write(Sample values) { - auto i = fields.begin(); - while (true) { - auto field = *i; - auto value = values.find(field); +void CsvSampleOutputStream::writeHeader() { + if (fields.size() == 0) { + return; + } - if (value != values.end()) { - stream << value->second; - } + auto i = fields.begin(); + while (i != fields.end()) { + stream << *i; i++; if (i != fields.end()) { stream << ","; - } else { - break; } } stream << endl; } +JsonSampleOutputStream::JsonSampleOutputStream(ostream &stream) : + stream(stream), fields(), filterFields(false) { +} + JsonSampleOutputStream::JsonSampleOutputStream(ostream &stream, vector<string> fields) : - stream(stream), fields(fields) { + stream(stream), fields(fields), filterFields(true) { } void JsonSampleOutputStream::write(Sample values) { json doc({}); - for (auto &f: fields) { - auto value = values.find(f); + if (filterFields) { + for (auto &f: fields) { + auto value = values.find(f); - if (value != values.end()) { - doc[f] = value->second; + if (value != values.end()) { + doc[f] = value->second; + } + } + } else { + for (auto &v: values) { + doc[v.first] = v.second; } } stream << doc << endl; } +SqlSampleOutputStream::SqlSampleOutputStream(ostream &stream) : + stream(stream), filterFields(false) { +} + SqlSampleOutputStream::SqlSampleOutputStream(ostream &stream, vector<string> fields) : - stream(stream), fields(fields) { + stream(stream), fields(fields), filterFields(true) { } void SqlSampleOutputStream::write(Sample values) { - auto i = fields.begin(); - stringstream fs, vs; - while (true) { - auto field = *i; + if (filterFields) { + auto i = fields.begin(); - fs << field; + while (i != fields.end()) { + auto field = *i; - auto value = values.find(field); + fs << field; - if (value != values.end()) { - vs << "'" << value->second << "'"; - } else { - vs << "NULL"; - } + auto value = values.find(field); - i++; + if (value != values.end()) { + vs << "'" << value->second << "'"; + } else { + vs << "NULL"; + } - if (i != fields.end()) { - fs << ","; - vs << ","; - } else { - break; + i++; + + if (i != fields.end()) { + fs << ","; + vs << ","; + } + } + } else { + auto i = values.begin(); + while (i != values.end()) { + auto v = *i++; + + fs << v.first; + vs << "'" << v.second << "'"; + + if (i != values.end()) { + fs << ","; + vs << ","; + } } } @@ -126,7 +172,6 @@ void CsvParser::process(mutable_buffers_1 buffer) { void CsvParser::process_line(shared_ptr<vector<uint8_t>> packet) { auto timestamp = std::chrono::system_clock::now().time_since_epoch().count(); auto s = std::string((char *) packet->data(), packet->size()); -// cerr << "packet: " << s << ", size=" << packet->size() << endl; static const boost::regex e("([_a-zA-Z0-9]+)=([0-9]+)"); @@ -142,17 +187,8 @@ void CsvParser::process_line(shared_ptr<vector<uint8_t>> packet) { auto value = static_cast<string>(what[2]); start = what[0].second; -// static const string device_type = "serial"; map<string, string> values; values[key] = value; -// values["hostname"] = hostname; -// values["device"] = device; -// values["device_type"] = device_type; -// values["timestamp"] = to_string(timestamp); -// values["sensor"] = sensor; -// values["value"] = value; - -// cerr << key << " => " << value << endl; sample[key] = value; diff --git a/apps/SoilMoistureIo.h b/apps/SoilMoistureIo.h index aff7c85..4edf4f4 100644 --- a/apps/SoilMoistureIo.h +++ b/apps/SoilMoistureIo.h @@ -34,7 +34,7 @@ public: return entries.end(); } - string& operator[](string key) { + string &operator[](string key) { return entries[key]; } @@ -49,34 +49,46 @@ public: class CsvSampleOutputStream : public SampleOutputStream { public: - CsvSampleOutputStream(ostream& stream, vector<string> fields); + CsvSampleOutputStream(ostream &stream); + + CsvSampleOutputStream(ostream &stream, vector<string> fields); void write(Sample values); private: - ostream& stream; + void writeHeader(); + + ostream &stream; + bool headerWritten; + bool filterFields; vector<string> fields; }; class JsonSampleOutputStream : public SampleOutputStream { public: - JsonSampleOutputStream(ostream& stream, vector<string> fields); + JsonSampleOutputStream(ostream &stream); + + JsonSampleOutputStream(ostream &stream, vector<string> fields); void write(Sample values); private: - ostream& stream; + ostream &stream; + bool filterFields; vector<string> fields; }; class SqlSampleOutputStream : public SampleOutputStream { public: - SqlSampleOutputStream(ostream& stream, vector<string> fields); + SqlSampleOutputStream(ostream &stream); + + SqlSampleOutputStream(ostream &stream, vector<string> fields); void write(Sample values); private: - ostream& stream; + ostream &stream; + bool filterFields; vector<string> fields; }; diff --git a/apps/sample-convert.cpp b/apps/sample-convert.cpp index 9a0627d..370059c 100644 --- a/apps/sample-convert.cpp +++ b/apps/sample-convert.cpp @@ -1,45 +1,7 @@ #include "SoilMoistureIo.h" #include "json.hpp" #include "apps.h" -#include <istream> - -enum class Format { - PLAIN, - JSON, - SQL -}; - -void validate(boost::any &v, const std::vector<std::string> &values, Format *, int) { - using namespace boost::program_options; - - const std::string &s = validators::get_single_string(values); - - if (s == "plain") { - v = boost::any(Format::PLAIN); - } else if (s == "json") { - v = boost::any(Format::JSON); - } else if (s == "sql") { - v = boost::any(Format::SQL); - } else { - throw validation_error(validation_error::invalid_option_value); - } -} - -namespace boost { - -template<> -std::string lexical_cast(const Format &arg) { - if (arg == Format::PLAIN) - return "plain"; - else if (arg == Format::JSON) - return "json"; - else if (arg == Format::SQL) - return "sql"; - else - throw std::runtime_error("Unknown format value: " + lexical_cast<std::string>(arg)); -} - -} +#include <fstream> namespace trygvis { namespace apps { @@ -49,17 +11,20 @@ using namespace trygvis::apps; using namespace trygvis::soil_moisture; namespace po = boost::program_options; -Format inputFormat, outputFormat; +string inputFile, inputFormat; +string outputFile, outputFormat; class sample_convert : public app { public: -// void add_options(po::options_description_easy_init &options) override { -// options -// ("help", "produce help message") -// ("input-format", po::value<Format>(&inputFormat)->default_value(Format::PLAIN)) -// ("output-format", po::value<Format>(&outputFormat)->default_value(Format::PLAIN)) -// } + void add_options(po::options_description_easy_init &options) override { + options + ("help", "produce this help message") + ("input", po::value<string>(&inputFile)->default_value("-")) + ("input-format", po::value<string>(&inputFormat)->default_value("csv")) + ("output", po::value<string>(&outputFile)->default_value("-")) + ("output-format", po::value<string>(&outputFormat)->default_value("plain")); + } int main(app_execution &execution) override { auto desc = execution.desc; @@ -67,31 +32,36 @@ public: shared_ptr<SampleOutputStream> output; - auto field_names = vector<string>({ - "hostname", - "device_type", - "device", - "timestamp", - "sensor", - "value" - }); - - field_names = vector<string>({ - "analog", - "dry", - "water", - "last_watering_started", - "last_watering_stopped", - "now" - }); - - output = make_shared<JsonSampleOutputStream>(cout, field_names); + istream *inputStream; + if (inputFile == "-") { + inputStream = &cin; + } else { + inputStream = new ifstream(inputFile); + } + + ostream *outputStream; + if (outputFile == "-") { + outputStream = &cout; + } else { + outputStream = new ofstream(outputFile); + } + + if (outputFormat == "plain") { + output = make_shared<CsvSampleOutputStream>(*outputStream); + } else if (outputFormat == "json") { + output = make_shared<JsonSampleOutputStream>(*outputStream); + } else if (outputFormat == "sql") { + output = make_shared<SqlSampleOutputStream>(*outputStream); + } else { + cerr << "Unsupported output format: " << outputFormat << endl; + return EXIT_FAILURE; + } auto input = make_shared<CsvParser>(output); char data[100]; - while (!cin.eof()) { - cin.get(data[0]); + while (!inputStream->eof()) { + inputStream->get(data[0]); input->process(boost::asio::buffer(data, 1)); } @@ -104,6 +74,7 @@ public: } using namespace trygvis::apps; + int main(int argc, char *argv[]) { sample_convert app; return launch_app(argc, argv, app); |