aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--apps/SoilMoistureIo.cpp81
-rw-r--r--apps/SoilMoistureIo.h15
-rw-r--r--apps/sample-convert.cpp27
-rw-r--r--apps/sm-get-value.cpp49
-rw-r--r--apps/sm-serial-read.cpp55
5 files changed, 106 insertions, 121 deletions
diff --git a/apps/SoilMoistureIo.cpp b/apps/SoilMoistureIo.cpp
index 40f2f7a..c7f3504 100644
--- a/apps/SoilMoistureIo.cpp
+++ b/apps/SoilMoistureIo.cpp
@@ -121,6 +121,54 @@ void JsonSampleOutputStream::write(SampleRecord sample) {
*stream.get() << doc << endl;
}
+KeyValueSampleOutputStream::KeyValueSampleOutputStream(shared_ptr<ostream> stream, KeyDictionary &dict) :
+ dict(dict), stream(move(stream)) {
+}
+
+void KeyValueSampleOutputStream::write(SampleRecord sample) {
+ // Skip empty records
+ if (sample.empty()) {
+ return;
+ }
+
+ auto &s = *stream.get();
+
+ bool first = true;
+ if (!dict.empty()) {
+ for (auto &key: dict) {
+ auto sampleKey = sample.dict.indexOf(key->name);
+
+ auto value = sample.at(sampleKey);
+
+ if (value) {
+ if (first) {
+ first = false;
+ } else {
+ s << ", ";
+ }
+ s << key->name << "=" << value;
+ }
+ }
+ } else {
+ for (auto &sampleKey: sample.dict) {
+ auto o = sample.at(sampleKey);
+
+ if (o) {
+ if (first) {
+ first = false;
+ } else {
+ s << ", ";
+ }
+ // Make sure that the key is registered in the dictionary
+ dict.indexOf(sampleKey->name);
+ s << sampleKey->name << "=" << o.get();
+ }
+ }
+ }
+
+ *stream.get() << endl;
+}
+
SqlSampleOutputStream::SqlSampleOutputStream(shared_ptr<ostream> stream, KeyDictionary &dict, string table_name) :
dict(dict), stream(move(stream)), table_name(table_name) {
}
@@ -257,6 +305,29 @@ string to_string(const sample_format_type &arg) {
throw std::runtime_error("Unknown format value: " + to_string(arg));
}
+std::ostream& operator<<(std::ostream& os, sample_format_type const& type){
+ return os << to_string(type);
+}
+
+std::istream& operator>>(std::istream& is, sample_format_type& type){
+ string s;
+ is >> s;
+
+ if (s == "auto") {
+ type = sample_format_type::AUTO;
+ } else if (s == "csv") {
+ type = sample_format_type::CSV;
+ } else if (s == "key-value") {
+ type = sample_format_type::KEY_VALUE;
+ } else if (s == "json") {
+ type = sample_format_type::JSON;
+ } else if (s == "sql") {
+ type = sample_format_type::SQL;
+ }
+
+ return is;
+}
+
unique_ptr<SampleStreamParser> open_sample_input_stream(
shared_ptr<SampleOutputStream> output,
KeyDictionary &dict,
@@ -266,7 +337,7 @@ unique_ptr<SampleStreamParser> open_sample_input_stream(
} else if (type == sample_format_type::AUTO) {
return make_unique<AutoSampleParser>(output, dict);
} else {
- throw sample_exception("Unsupported format type: " + to_string(type));
+ throw sample_exception("No parser for format type: " + to_string(type));
}
}
@@ -276,13 +347,15 @@ unique_ptr<SampleOutputStream> open_sample_output_stream(
sample_format_type type) {
if (type == sample_format_type::CSV) {
- return make_unique<CsvSampleOutputStream>(move(output), dict);
+ return make_unique<CsvSampleOutputStream>(output, dict);
+ } else if (type == sample_format_type::KEY_VALUE) {
+ return make_unique<KeyValueSampleOutputStream>(output, dict);
} else if (type == sample_format_type::JSON) {
- return make_unique<JsonSampleOutputStream>(move(output), dict);
+ return make_unique<JsonSampleOutputStream>(output, dict);
// } else if (type == sample_format_type::SQL) {
// return make_unique<SqlSampleOutputStream>(dict, move(output), table_name);
} else {
- throw sample_exception("Unsupported format type: " + to_string(type));
+ throw sample_exception("No writer for format type: " + to_string(type));
}
}
diff --git a/apps/SoilMoistureIo.h b/apps/SoilMoistureIo.h
index 5324114..3e78f46 100644
--- a/apps/SoilMoistureIo.h
+++ b/apps/SoilMoistureIo.h
@@ -31,6 +31,10 @@ enum class sample_format_type {
string to_string(const sample_format_type &arg);
+std::ostream& operator<<(std::ostream& os, sample_format_type const& type);
+
+std::istream& operator>>(std::istream& is, sample_format_type& type);
+
class SampleStreamParser;
class SampleOutputStream;
@@ -270,6 +274,17 @@ private:
shared_ptr<ostream> stream;
};
+class KeyValueSampleOutputStream : public SampleOutputStream {
+public:
+ KeyValueSampleOutputStream(shared_ptr<ostream> stream, KeyDictionary &dict);
+
+ void write(SampleRecord sample) override;
+
+private:
+ KeyDictionary &dict;
+ shared_ptr<ostream> stream;
+};
+
class SqlSampleOutputStream : public SampleOutputStream {
public:
SqlSampleOutputStream(shared_ptr<ostream> stream, KeyDictionary &dict, string table_name);
diff --git a/apps/sample-convert.cpp b/apps/sample-convert.cpp
index 79d6f1c..c6f5d01 100644
--- a/apps/sample-convert.cpp
+++ b/apps/sample-convert.cpp
@@ -18,9 +18,9 @@ public:
options
("help", "produce this help message")
("input", po::value<string>(&input_file)->default_value("-"))
- ("input-format", po::value<string>(&input_format)->default_value("csv"))
+// ("input-format", po::value<string>(&input_format)->default_value("csv"))
("output", po::value<string>(&output_file)->default_value("-"))
- ("output-format", po::value<string>(&output_format)->default_value("plain"));
+ ("output-format", po::value<sample_format_type>(&output_format)->default_value(sample_format_type::KEY_VALUE));
}
void add_extra_options(po::options_description &all_options) override {
@@ -36,7 +36,6 @@ public:
auto vm = execution.vm;
KeyDictionary dict;
- shared_ptr<SampleOutputStream> output;
istream *inputStream;
if (input_file == "-") {
@@ -60,23 +59,7 @@ public:
}
}
- if (output_format == "plain") {
- output = make_shared<CsvSampleOutputStream>(outputStream, dict);
- } else if (output_format == "json") {
- output = make_shared<JsonSampleOutputStream>(outputStream, dict);
- } else if (output_format == "sql") {
- if (table_name.size() == 0) {
- cerr << "Missing option: table-name" << endl;
- return EXIT_FAILURE;
- }
-
- output = make_shared<SqlSampleOutputStream>(outputStream, dict, table_name);
- } else if (output_format == "csv") {
- output = make_shared<CsvSampleOutputStream>(outputStream, dict);
- } else {
- cerr << "Unsupported output format: " << output_format << endl;
- return EXIT_FAILURE;
- }
+ shared_ptr<SampleOutputStream> output = open_sample_output_stream(outputStream, dict, output_format);
auto input = make_shared<KeyValueSampleParser>(output, dict);
@@ -90,8 +73,8 @@ public:
}
private:
- string input_file, input_format;
- string output_file, output_format;
+ string input_file, output_file;
+ sample_format_type output_format;
string table_name;
};
diff --git a/apps/sm-get-value.cpp b/apps/sm-get-value.cpp
index a61132f..9f9308a 100644
--- a/apps/sm-get-value.cpp
+++ b/apps/sm-get-value.cpp
@@ -5,6 +5,7 @@
#include <thread>
#include "ble/Bluetooth.h"
#include "SoilMoisture.h"
+#include "SoilMoistureIo.h"
#include "json.hpp"
#include "apps.h"
@@ -17,46 +18,8 @@ using namespace trygvis::soil_moisture;
namespace po = boost::program_options;
using json = nlohmann::json;
-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<>
-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<string>(arg));
-}
-
-}
-
bool loop;
-Format format;
+sample_format_type format;
time_point<system_clock> targetTime;
unsigned int sleepTime;
vector<unsigned int> sensors;
@@ -92,19 +55,19 @@ void withConnection(BluetoothGatt &gatt) {
auto timestamp = duration_cast<seconds>(epoch).count();
uint16_t value = soilMoisture.getValue((uint8_t) sensor);
- if (format == Format::PLAIN) {
+ if (format == sample_format_type::KEY_VALUE) {
cout << "device=" << device.str()
<< ", sensor=" << to_string(sensor)
<< ", timestamp=" << to_string(timestamp)
<< ", value=" << (int) value << endl;
- } else if (format == Format::JSON) {
+ } else if (format == sample_format_type::JSON) {
json j;
j["device"] = device.str();
j["sensor"] = sensor;
j["timestamp"] = timestamp;
j["value"] = value;
cout << j << endl;
- } else if (format == Format::SQL) {
+ } else if (format == sample_format_type::SQL) {
cout << "INSERT INTO soil_moisture_sample(device, sensor, timestamp, value) VALUES("
<< "'" << device.str() << "', "
<< sensor << ", "
@@ -131,7 +94,7 @@ public:
("sensor", po::value<vector<unsigned int>>(&sensors)->multitoken(), "Sensor to poll, defaults to all")
("sleep", po::value<unsigned int>(&sleepTime)->default_value(0),
"How long to sleep in seconds between each poll. If not give, it will exit after first poll")
- ("format", po::value<Format>(&format)->default_value(Format::PLAIN), "Output format");
+ ("format", po::value<sample_format_type>(&format)->default_value(sample_format_type::KEY_VALUE), "Output format");
}
diff --git a/apps/sm-serial-read.cpp b/apps/sm-serial-read.cpp
index 10981e0..aa997e5 100644
--- a/apps/sm-serial-read.cpp
+++ b/apps/sm-serial-read.cpp
@@ -5,44 +5,6 @@
#include <thread>
#include <boost/asio/serial_port.hpp>
-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));
-}
-
-}
-
namespace trygvis {
namespace apps {
@@ -54,7 +16,7 @@ using namespace trygvis::soil_moisture;
namespace po = boost::program_options;
using json = nlohmann::json;
-Format format;
+sample_format_type format;
string hostname = get_hostname();
class port_handler {
@@ -94,7 +56,7 @@ public:
options
("help", "produce help message")
("port", po::value<string>()->required(), "The serial port to read")
- ("format", po::value<Format>(&format)->default_value(Format::PLAIN), "Output format");
+ ("format", po::value<sample_format_type>(&format)->default_value(sample_format_type::KEY_VALUE), "Output format");
}
int main(app_execution &execution) override {
@@ -122,19 +84,8 @@ public:
cerr << "port is not open" << endl;
}
- shared_ptr<SampleOutputStream> output;
shared_ptr<ostream> outputStream = shared_ptr<ostream>(&cout, noop_deleter);
-
- if (format == Format::JSON) {
- output = make_shared<JsonSampleOutputStream>(outputStream, dict);
- } else if (format == Format::SQL) {
- output = make_shared<SqlSampleOutputStream>(outputStream, dict, "raw");
- } else if (format == Format::PLAIN) {
- output = make_shared<CsvSampleOutputStream>(outputStream, dict);
- } else {
- cerr << "Unsupported format: " << boost::lexical_cast<string>(format) << endl;
- return EXIT_FAILURE;
- }
+ shared_ptr<SampleOutputStream> output = open_sample_output_stream(outputStream, dict, format);
shared_ptr<KeyValueSampleParser> input = make_shared<KeyValueSampleParser>(output, dict);