aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTrygve Laugstøl <trygvis@inamo.no>2015-03-06 21:07:34 +0100
committerTrygve Laugstøl <trygvis@inamo.no>2015-03-06 21:07:34 +0100
commita83e6f5960a549d54991495336bd12d549127d91 (patch)
treed21f63b718d65074215fff6edbbcdbd5f26caf4a
parente03bf5d416776cb5ea27c7354657920939c04e71 (diff)
downloadble-toys-a83e6f5960a549d54991495336bd12d549127d91.tar.gz
ble-toys-a83e6f5960a549d54991495336bd12d549127d91.tar.bz2
ble-toys-a83e6f5960a549d54991495336bd12d549127d91.tar.xz
ble-toys-a83e6f5960a549d54991495336bd12d549127d91.zip
o Starting on a tool to convert between sample formats.
-rw-r--r--.gitignore1
-rw-r--r--apps/CMakeLists.txt2
-rw-r--r--apps/SoilMoistureIo.cpp107
-rw-r--r--apps/SoilMoistureIo.h56
-rw-r--r--apps/sample-convert.cpp98
-rw-r--r--apps/sm-serial-read.cpp81
-rw-r--r--sample.json6
7 files changed, 317 insertions, 34 deletions
diff --git a/.gitignore b/.gitignore
index 5331b61..cdaf6a8 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,4 @@
noble
.idea
build
+*.log
diff --git a/apps/CMakeLists.txt b/apps/CMakeLists.txt
index 7fa9443..ec45a6b 100644
--- a/apps/CMakeLists.txt
+++ b/apps/CMakeLists.txt
@@ -1,5 +1,6 @@
set(APPS
ble-inspect-device
+ sample-convert
sm-db-insert
sm-db-select
sm-get-value
@@ -8,6 +9,7 @@ set(APPS
list(APPEND APPS log4cplus-test)
list(APPEND shared_sources SoilMoisture.cpp)
+list(APPEND shared_sources SoilMoistureIo.cpp)
list(APPEND shared_sources apps.cpp)
# Boost
diff --git a/apps/SoilMoistureIo.cpp b/apps/SoilMoistureIo.cpp
new file mode 100644
index 0000000..1280fc6
--- /dev/null
+++ b/apps/SoilMoistureIo.cpp
@@ -0,0 +1,107 @@
+#include "SoilMoistureIo.h"
+
+#include "json.hpp"
+#include <set>
+
+namespace trygvis {
+namespace soil_moisture {
+
+using namespace std;
+using json = nlohmann::json;
+
+CsvSampleOutputStream::CsvSampleOutputStream(ostream &stream, vector<string> fields) :
+ stream(stream), fields(fields) {
+
+ auto i = fields.begin();
+ while (true) {
+ stream << *i;
+
+ i++;
+
+ if (i != fields.end()) {
+ stream << ",";
+ } else {
+ break;
+ }
+ }
+
+ stream << endl;
+}
+
+void CsvSampleOutputStream::write(it values) {
+ auto i = fields.begin();
+ while (true) {
+ auto field = *i;
+ auto value = values.find(field);
+
+ if (value != values.end()) {
+ stream << value->second;
+ }
+
+ i++;
+
+ if (i != fields.end()) {
+ stream << ",";
+ } else {
+ break;
+ }
+ }
+
+ stream << endl;
+}
+
+JsonSampleOutputStream::JsonSampleOutputStream(ostream &stream, vector<string> fields) :
+ stream(stream), fields(fields) {
+}
+
+void JsonSampleOutputStream::write(it values) {
+ json doc;
+
+ for (auto &f: fields) {
+ auto value = values.find(f);
+
+ if (value != values.end()) {
+ doc[f] = value->second;
+ }
+ }
+
+ stream << doc << endl;
+}
+
+SqlSampleOutputStream::SqlSampleOutputStream(ostream &stream, vector<string> fields) :
+ stream(stream), fields(fields) {
+}
+
+void SqlSampleOutputStream::write(it values) {
+ auto i = fields.begin();
+
+ stringstream fs, vs;
+
+ while (true) {
+ auto field = *i;
+
+ fs << field;
+
+ auto value = values.find(field);
+
+ if (value != values.end()) {
+ vs << "'" << value->second << "'";
+ } else {
+ vs << "NULL";
+ }
+
+ i++;
+
+ if (i != fields.end()) {
+ fs << ",";
+ vs << ",";
+ } else {
+ break;
+ }
+ }
+
+ stream << "INSERT INTO (" << fs << ") VALUES(" << vs << ")" << endl;
+}
+
+}
+}
diff --git a/apps/SoilMoistureIo.h b/apps/SoilMoistureIo.h
new file mode 100644
index 0000000..739758a
--- /dev/null
+++ b/apps/SoilMoistureIo.h
@@ -0,0 +1,56 @@
+#ifndef SOIL_MOISTURE_IO_H
+#define SOIL_MOISTURE_IO_H
+
+#include <ostream>
+#include <vector>
+#include <map>
+
+namespace trygvis {
+namespace soil_moisture {
+
+using namespace std;
+
+class SampleOutputStream {
+public:
+ typedef map<string, string> it;
+
+ virtual void write(it values) = 0;
+};
+
+class CsvSampleOutputStream : public SampleOutputStream {
+public:
+ CsvSampleOutputStream(ostream& stream, vector<string> fields);
+
+ void write(it values);
+
+private:
+ ostream& stream;
+ vector<string> fields;
+};
+
+class JsonSampleOutputStream : public SampleOutputStream {
+public:
+ JsonSampleOutputStream(ostream& stream, vector<string> fields);
+
+ void write(it values);
+
+private:
+ ostream& stream;
+ vector<string> fields;
+};
+
+class SqlSampleOutputStream : public SampleOutputStream {
+public:
+ SqlSampleOutputStream(ostream& stream, vector<string> fields);
+
+ void write(it values);
+
+private:
+ ostream& stream;
+ vector<string> fields;
+};
+
+}
+}
+
+#endif
diff --git a/apps/sample-convert.cpp b/apps/sample-convert.cpp
new file mode 100644
index 0000000..2e448ca
--- /dev/null
+++ b/apps/sample-convert.cpp
@@ -0,0 +1,98 @@
+#include "SoilMoistureIo.h"
+#include "json.hpp"
+#include "apps.h"
+
+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 {
+
+using namespace std;
+using namespace trygvis::apps;
+using namespace trygvis::soil_moisture;
+namespace po = boost::program_options;
+
+Format inputFormat, 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))
+// }
+
+ int main(app_execution &execution) override {
+ auto desc = execution.desc;
+ auto vm = execution.vm;
+
+ shared_ptr<SampleOutputStream> sampleStream;
+
+ auto field_names = vector<string>({
+ "hostname",
+ "device_type",
+ "device",
+ "timestamp",
+ "sensor",
+ "value"
+ });
+
+ sampleStream = make_shared<JsonSampleOutputStream>(cout, field_names);
+
+ map<string, string> values;
+ values["hostname"] = "my-hostname";
+ values["extra"] = "wat";
+
+ sampleStream->write(values);
+
+ return EXIT_SUCCESS;
+ }
+
+};
+
+}
+}
+
+using namespace trygvis::apps;
+int main(int argc, char *argv[]) {
+ sample_convert app;
+ return launch_app(argc, argv, app);
+}
diff --git a/apps/sm-serial-read.cpp b/apps/sm-serial-read.cpp
index f1cabad..d605ae4 100644
--- a/apps/sm-serial-read.cpp
+++ b/apps/sm-serial-read.cpp
@@ -1,12 +1,10 @@
-#include <iostream>
-#include <iomanip>
+#include "SoilMoistureIo.h"
+#include "json.hpp"
+#include "apps.h"
#include <chrono>
#include <thread>
#include <boost/asio/serial_port.hpp>
#include <boost/regex.hpp>
-#include <chrono>
-#include "json.hpp"
-#include "apps.h"
enum class Format {
PLAIN,
@@ -53,6 +51,7 @@ using namespace boost::asio;
using namespace std;
using namespace std::chrono;
using namespace trygvis::apps;
+using namespace trygvis::soil_moisture;
namespace po = boost::program_options;
using json = nlohmann::json;
@@ -61,8 +60,8 @@ string hostname = get_hostname();
class port_handler {
public:
- port_handler(string device, serial_port &serial_port) :
- device(device), port(serial_port) {
+ port_handler(string device, serial_port &serial_port, shared_ptr<SampleOutputStream> stream) :
+ device(device), port(serial_port), stream(stream) {
}
void run() {
@@ -86,12 +85,11 @@ public:
}
void on_packet(shared_ptr<vector<uint8_t>> packet) {
-// std::chrono::time_point<std::chrono::system_clock> ;
auto timestamp = std::chrono::system_clock::now().time_since_epoch().count();
auto s = std::string((char *) packet->data(), packet->size());
cerr << "packet: " << s << endl;
- static const boost::regex e("#(\\d+) = (\\d+)");
+ static const boost::regex e("([a-zA-Z0-9]+)=([0-9]+)");
std::string::const_iterator start = s.begin();
std::string::const_iterator end = s.end();
@@ -104,27 +102,17 @@ public:
start = what[0].second;
static const string device_type = "serial";
- if (format == Format::JSON) {
- json j;
- j["hostname"] = hostname;
- j["device_type"] = device_type;
- j["device"] = device;
- j["timestamp"] = timestamp;
- j["sensor"] = sensor;
- j["value"] = value;
- cout << j << endl;
- } else if (format == Format::SQL) {
- cout << "INSERT INTO serial_sample(hostname, device_type, device, sensor, timestamp, value) VALUES("
- << hostname << ", "
- << device_type << ", "
- << device << ", "
- << timestamp << ", "
- << sensor << ", "
- << value << ");"
- << endl;
- } else { // plain
- cout << "sensor #" << sensor << " = " << value << endl;
- }
+ map<string, string> values;
+ values["hostname"] = hostname;
+ values["device_type"] = device_type;
+ values["device"] = device;
+ values["timestamp"] = to_string(timestamp);
+ values["sensor"] = sensor;
+ values["value"] = value;
+
+// cerr << sensor << " => " << value << endl;
+
+ stream->write(values);
flags |= boost::match_prev_avail;
flags |= boost::match_not_bob;
@@ -138,6 +126,8 @@ private:
serial_port &port;
uint8_t data[size];
mutable_buffers_1 buffer = boost::asio::buffer(data, size);
+
+ shared_ptr<SampleOutputStream> stream;
};
class sm_serial_read : public app {
@@ -159,12 +149,13 @@ public:
io_service io_service;
- serial_port port(io_service, port_name);
+ serial_port port(io_service);
+ port.open(port_name);
port.set_option(serial_port_base::baud_rate(baud_rate));
port.set_option(serial_port_base::character_size(8));
+ port.set_option(serial_port_base::parity(serial_port_base::parity::none));
+ port.set_option(serial_port_base::stop_bits(serial_port_base::stop_bits::one));
port.set_option(serial_port_base::flow_control(serial_port_base::flow_control::none));
- port.set_option(serial_port_base::parity(serial_port_base::parity::even));
- port.set_option(serial_port_base::stop_bits(serial_port_base::stop_bits::two));
if (port.is_open()) {
cerr << "port is open" << endl;
@@ -172,7 +163,28 @@ public:
cerr << "port is not open" << endl;
}
- port_handler(port_name, port).run();
+ auto field_names = vector<string>({
+ "hostname",
+ "device_type",
+ "device",
+ "timestamp",
+ "sensor",
+ "value"
+ });
+ shared_ptr <SampleOutputStream> sampleStream;
+
+ if (format == Format::JSON) {
+ sampleStream = make_shared<JsonSampleOutputStream>(cout, field_names);
+ } else if (format == Format::SQL) {
+ sampleStream = make_shared<SqlSampleOutputStream>(cout, field_names);
+ } else if (format == Format::PLAIN) {
+ sampleStream = make_shared<CsvSampleOutputStream>(cout, field_names);
+ } else {
+ cerr << "Unsupported format: " << boost::lexical_cast<string>(format) << endl;
+ return EXIT_FAILURE;
+ }
+
+ port_handler(port_name, port, sampleStream).run();
return EXIT_SUCCESS;
}
@@ -183,6 +195,7 @@ public:
}
using namespace trygvis::apps;
+
int main(int argc, char *argv[]) {
sm_serial_read app;
return launch_app(argc, argv, app);
diff --git a/sample.json b/sample.json
new file mode 100644
index 0000000..3ff82d4
--- /dev/null
+++ b/sample.json
@@ -0,0 +1,6 @@
+{
+ "mac": "aa:bb:cc:dd:ee:ff",
+ "sensor": 1,
+ "timestamp": 124,
+ "value": 123
+}