From c56840f03cf139d60c6d90b55cf16e70f6ae2bc2 Mon Sep 17 00:00:00 2001 From: Trygve Laugstøl Date: Sun, 21 Jun 2015 00:15:04 +0200 Subject: o Going all header file based and single-executable to launch all apps. o Ading CMake magic to generate the launcher --- apps/CMakeLists.txt | 59 +++++++++----- apps/apps.cpp | 63 --------------- apps/apps.h | 40 +++++++--- apps/ble-inspect-device.cpp | 58 -------------- apps/ble-inspect-device.h | 79 +++++++++++++++++++ apps/generate.cpp | 71 +++++++++++++++++ apps/launcher.cpp | 94 +++++++++++++++++++++++ apps/log4cplus-test.cpp | 50 ------------ apps/sample-add-timestamp.cpp | 105 ------------------------- apps/sample-add-timestamp.h | 99 ++++++++++++++++++++++++ apps/sample-convert.cpp | 128 ------------------------------ apps/sample-convert.h | 121 +++++++++++++++++++++++++++++ apps/sample-select.cpp | 128 ------------------------------ apps/sample-select.h | 121 +++++++++++++++++++++++++++++ apps/sm-db-insert.cpp | 78 ------------------- apps/sm-db-insert.h | 80 +++++++++++++++++++ apps/sm-db-select.cpp | 82 -------------------- apps/sm-db-select.h | 81 +++++++++++++++++++ apps/sm-get-value.cpp | 159 -------------------------------------- apps/sm-get-value.h | 153 ++++++++++++++++++++++++++++++++++++ apps/sm-serial-read-all.cpp | 175 ------------------------------------------ apps/sm-serial-read-all.h | 171 +++++++++++++++++++++++++++++++++++++++++ apps/sm-serial-read.cpp | 114 --------------------------- apps/sm-serial-read.h | 111 +++++++++++++++++++++++++++ 24 files changed, 1251 insertions(+), 1169 deletions(-) delete mode 100644 apps/ble-inspect-device.cpp create mode 100644 apps/ble-inspect-device.h create mode 100644 apps/generate.cpp create mode 100644 apps/launcher.cpp delete mode 100644 apps/log4cplus-test.cpp delete mode 100644 apps/sample-add-timestamp.cpp create mode 100644 apps/sample-add-timestamp.h delete mode 100644 apps/sample-convert.cpp create mode 100644 apps/sample-convert.h delete mode 100644 apps/sample-select.cpp create mode 100644 apps/sample-select.h delete mode 100644 apps/sm-db-insert.cpp create mode 100644 apps/sm-db-insert.h delete mode 100644 apps/sm-db-select.cpp create mode 100644 apps/sm-db-select.h delete mode 100644 apps/sm-get-value.cpp create mode 100644 apps/sm-get-value.h delete mode 100644 apps/sm-serial-read-all.cpp create mode 100644 apps/sm-serial-read-all.h delete mode 100644 apps/sm-serial-read.cpp create mode 100644 apps/sm-serial-read.h (limited to 'apps') diff --git a/apps/CMakeLists.txt b/apps/CMakeLists.txt index 988a989..c9eea9e 100644 --- a/apps/CMakeLists.txt +++ b/apps/CMakeLists.txt @@ -1,4 +1,3 @@ -list(APPEND APPS log4cplus-test) list(APPEND APPS ble-inspect-device) list(APPEND APPS sample-add-timestamp) list(APPEND APPS sample-convert) @@ -9,10 +8,6 @@ list(APPEND APPS sm-get-value) list(APPEND APPS sm-serial-read) list(APPEND APPS sm-serial-read-all) -add_library(trygvis-apps - SoilMoisture.cpp - apps.cpp) - # Boost find_package(Boost COMPONENTS regex system program_options REQUIRED) @@ -34,20 +29,44 @@ if(LOG4CPLUS_LIBRARIES MATCHES NOTFOUND) message(FATAL_ERROR "Could not find log4cplus library files") endif() -include_directories("${PROJECT_SOURCE_DIR}/include") -include_directories("${PROJECT_SOURCE_DIR}/json/src") -include_directories("${PROJECT_SOURCE_DIR}/sensor/include") -include_directories("${LOG4CPLUS_INCLUDE_DIRECTORIES}") +add_executable(generate generate.cpp) + +file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/generated) + +add_custom_command( + OUTPUT generated/apps-list.gen.h + COMMAND generate ${CMAKE_CURRENT_BINARY_DIR}/generated/apps-list.gen.h ${APPS} + DEPENDS generate ${CMAKE_CURRENT_SOURCE_DIR}/CMakeLists.txt) + +foreach(app ${APPS}) + list(APPEND APPS_SOURCES ${app}.h) +endforeach() + +list(APPEND SOURCES ${CMAKE_CURRENT_BINARY_DIR}/generated/apps-list.gen.h) +list(APPEND SOURCES SoilMoisture.cpp SoilMoisture.h) +list(APPEND SOURCES apps.cpp apps.h) + +add_executable(launcher launcher.cpp + ${SOURCES} + ${APPS_SOURCES}) +target_include_directories(launcher + PUBLIC ${CMAKE_CURRENT_BINARY_DIR}/generated + PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} + PUBLIC "${PROJECT_SOURCE_DIR}/include" + PUBLIC "${PROJECT_SOURCE_DIR}/json/src" + PUBLIC "${PROJECT_SOURCE_DIR}/sensor/include" + PUBLIC "${LOG4CPLUS_INCLUDE_DIRECTORIES}") + +target_link_libraries(launcher ble) +target_link_libraries(launcher trygvis-sensor) +target_link_libraries(launcher ${Boost_LIBRARIES}) +target_link_libraries(launcher ${BLUEZ_LIBRARIES}) +target_link_libraries(launcher ${PQXX_LIBRARIES}) +target_link_libraries(launcher ${LOG4CPLUS_LIBRARIES}) +target_link_libraries(launcher ${CMAKE_THREAD_LIBS_INIT}) foreach(app ${APPS}) - add_executable(${app} ${app}.cpp) - - target_link_libraries(${app} ble) - target_link_libraries(${app} trygvis-apps) - target_link_libraries(${app} trygvis-sensor) - target_link_libraries(${app} ${Boost_LIBRARIES}) - target_link_libraries(${app} ${BLUEZ_LIBRARIES}) - target_link_libraries(${app} ${PQXX_LIBRARIES}) - target_link_libraries(${app} ${LOG4CPLUS_LIBRARIES}) - target_link_libraries(${app} ${CMAKE_THREAD_LIBS_INIT}) -endforeach(app) + add_custom_command( + TARGET launcher POST_BUILD + COMMAND ln -sf launcher ${app}) +endforeach() diff --git a/apps/apps.cpp b/apps/apps.cpp index b9ee81c..783c0b0 100644 --- a/apps/apps.cpp +++ b/apps/apps.cpp @@ -1,8 +1,4 @@ #include "apps.h" -#include -#include -#include -#include #include namespace trygvis { @@ -12,65 +8,6 @@ using namespace log4cplus; using namespace std; namespace po = boost::program_options; -const po::options_description logging_options() { - po::options_description desc; - - return desc; -} - -void setup_logging(po::variables_map vm) { - Appender *console = new ConsoleAppender(true, true); - PatternLayout* layout = new PatternLayout(LOG4CPLUS_TEXT("%-5p %r %-20c %m%n")); - console->setLayout(auto_ptr(layout)); - - Hierarchy &h = Logger::getDefaultHierarchy(); - h.getRoot().addAppender(SharedAppenderPtr(console)); -} - -int launch_app(int argc, char *argv[], app &app) { - po::options_description all("Options"); - - auto all_options = all.add_options(); - app.add_options(all_options); - - all.add(logging_options()); - app.add_extra_options(all); - - po::variables_map vm; - - try { - auto parsed = po::parse_command_line(argc, argv, all); - po::store(parsed, vm); - - po::notify(vm); - - auto unrecognized = po::collect_unrecognized(parsed.options, po::include_positional); - - if (vm.count("help")) { - cerr << all << "\n"; - return EXIT_FAILURE; - } - - if (unrecognized.size()) { - cerr << "Unrecognized option: " << unrecognized.at(0) << "\n"; - return EXIT_FAILURE; - } - - setup_logging(vm); - - app_execution execution(all, vm); - - return app.main(execution); - } catch (po::required_option &e) { - cerr << "Missing required option: " << e.get_option_name() << endl; - cerr << all << endl; - return EXIT_FAILURE; - } catch (po::unknown_option &e) { - cerr << e.what() << endl; - return EXIT_FAILURE; - } -} - std::string get_hostname() { struct addrinfo hints, *info, *p; // int gai_result; diff --git a/apps/apps.h b/apps/apps.h index 8173c51..7569a9f 100644 --- a/apps/apps.h +++ b/apps/apps.h @@ -3,27 +3,53 @@ #include #include #include +#include "json.hpp" +#include namespace trygvis { namespace apps { namespace po = boost::program_options; +using namespace log4cplus; + +using json = nlohmann::json; + +class missing_key : public std::runtime_error { +public: + missing_key(const json::string_t key) : runtime_error("Missing key: " + key), key(key) { + } + + const json::string_t key; +}; + +template +T get(json j, std::string key) { + auto ref = j[key]; + + if (ref.is_null()) { + throw missing_key(key); + } + + return ref; +} class app_execution { public: - app_execution(po::options_description desc, po::variables_map vm) : desc(desc), vm(vm) { + app_execution(po::options_description desc, po::variables_map vm, Logger logger) : desc(desc), vm(vm), logger(logger) { } - po::options_description desc; - po::variables_map vm; + const po::options_description desc; + const po::variables_map vm; + const log4cplus::Logger logger; void usage(); }; class app { public: - app(std::string app_name): _app_name(app_name), logger(log4cplus::Logger::getInstance(LOG4CPLUS_TEXT(app_name))) { + app(std::string app_name) : app_name(app_name) { } + virtual ~app() = default; virtual void add_options(po::options_description_easy_init &options) { @@ -34,13 +60,9 @@ public: virtual int main(app_execution &execution) = 0; -protected: - const std::string _app_name; - const log4cplus::Logger logger; + const std::string app_name; }; -int launch_app(int argc, char *argv[], app &app); - std::string get_hostname(); template diff --git a/apps/ble-inspect-device.cpp b/apps/ble-inspect-device.cpp deleted file mode 100644 index e4e7113..0000000 --- a/apps/ble-inspect-device.cpp +++ /dev/null @@ -1,58 +0,0 @@ -#include -#include -#include -#include -#include "ble/Bluetooth.h" - -using namespace std; -using namespace trygvis::bluetooth; - -void scan_callback(BluetoothDevice &device) { - cout << "Inspecting device: " << device.getMac().str() << endl; - - auto &gatt = device.connectGatt(); - - gatt.discoverServices(); - - vector services = gatt.getServices(); - cout << "Device has " << services.size() << " services" << endl; - - for (auto &s: services) { - const vector characteristics = s->getCharacteristics(); - - cout << "Service: UUID: " << s->getUuid() << ", has " << characteristics.size() << " characteristics" << endl; - - for (auto &c: characteristics) { - cout << "Characteristic: UUID: " << c->getUuid() << ", properties: " << (int) c->getProperties() << endl; - } - } - - gatt.disconnect(); -} - -int main(int argc, char *argv[]) { - if (argc != 2) { - cerr << "usage: " << argv[0] << " [mac]" << endl; - return EXIT_FAILURE; - } - - BluetoothSystem bluetoothSystem; - - try { - Mac mac = Mac::parseMac(argv[1]); - - BluetoothAdapter &adapter = getAdapter(0); - - BluetoothDevice &device = adapter.getDevice(mac); - - scan_callback(device); - - return EXIT_SUCCESS; - } catch (std::runtime_error ex) { - cout << "std::runtime_error: " << ex.what() << endl; - return EXIT_FAILURE; - } catch (std::exception ex) { - cout << "std::exception: " << ex.what() << endl; - return EXIT_FAILURE; - } -} diff --git a/apps/ble-inspect-device.h b/apps/ble-inspect-device.h new file mode 100644 index 0000000..bf79b2c --- /dev/null +++ b/apps/ble-inspect-device.h @@ -0,0 +1,79 @@ +#include +#include +#include +#include +#include "ble/Bluetooth.h" +#include "apps.h" + +namespace trygvis { +namespace apps { + +using namespace std; +using namespace trygvis::bluetooth; +using namespace trygvis::apps; + +class ble_inspect_device : public app { + +public: + ble_inspect_device() : app("ble-inspect-device") { + } + + ~ble_inspect_device() = default; + + void add_options(po::options_description_easy_init &options) override { + options + ("device", po::value()->required(), "The MAC of the device to inspect"); + } + + void scan_callback(BluetoothDevice &device) { + cout << "Inspecting device: " << device.getMac().str() << endl; + + auto &gatt = device.connectGatt(); + + gatt.discoverServices(); + + vector < BluetoothGattService * > services = gatt.getServices(); + cout << "Device has " << services.size() << " services" << endl; + + for (auto &s: services) { + const vector characteristics = s->getCharacteristics(); + + cout << "Service: UUID: " << s->getUuid() << ", has " << characteristics.size() << " characteristics" << + endl; + + for (auto &c: characteristics) { + cout << "Characteristic: UUID: " << c->getUuid() << ", properties: " << (int) c->getProperties() << + endl; + } + } + + gatt.disconnect(); + } + + int main(app_execution &execution) override { + string mac_str = execution.vm["mac"].as(); + + BluetoothSystem bluetoothSystem; + + try { + Mac mac = Mac::parseMac(mac_str); + + BluetoothAdapter &adapter = getAdapter(0); + + BluetoothDevice &device = adapter.getDevice(mac); + + scan_callback(device); + + return EXIT_SUCCESS; + } catch (std::runtime_error ex) { + cout << "std::runtime_error: " << ex.what() << endl; + return EXIT_FAILURE; + } catch (std::exception ex) { + cout << "std::exception: " << ex.what() << endl; + return EXIT_FAILURE; + } + } +}; + +} +} diff --git a/apps/generate.cpp b/apps/generate.cpp new file mode 100644 index 0000000..f0bf0fd --- /dev/null +++ b/apps/generate.cpp @@ -0,0 +1,71 @@ +#include +#include +#include +#include + +using namespace std; + +int main(int argc, char *argv[]) { + + cout << "Generating " << argv[1] << endl; + + ofstream out; + + out.open(argv[1], ofstream::out); + + if (!out.is_open()) { + return EXIT_FAILURE; + } + + out << "#pragma once" << endl + << "#include " << endl + << endl; + + vector> apps; + + regex r("-"); + for (int i = 2; i < argc; i++) { + string app_name = argv[i]; + stringstream buf; + + regex_replace(ostream_iterator(buf), + app_name.begin(), app_name.end(), + r, "_"); + string class_name = buf.str(); + + apps.emplace_back(make_pair(app_name, class_name)); + } + + for_each(begin(apps), end(apps), [&](pair pair) { +// out << "class " << pair.second << ";" << endl; + out << "#include \"" << pair.first << ".h\"" << endl; + }); + out << endl; + + bool first = true; + + out << "template" << endl + << "int launch_app(int argc, const char *argv[]);" + << endl; + + out << "int launch(const std::string app_name, int argc, const char *argv[]) {" << endl; + + for_each(begin(apps), end(apps), [&](auto pair) { + out << " "; + if (!first) { + out << "} else "; + } else { + first = false; + } + + out << "if (app_name == \"" << pair.first << "\") {" << endl + << " return launch_app<" << pair.second << ">(argc, argv);" << endl; + }); + + out << " } else {" << endl + << " return EXIT_FAILURE;" << endl + << " }" << endl + << "}" << endl; + + return EXIT_SUCCESS; +} diff --git a/apps/launcher.cpp b/apps/launcher.cpp new file mode 100644 index 0000000..dc31abd --- /dev/null +++ b/apps/launcher.cpp @@ -0,0 +1,94 @@ +#include "apps.h" +#include "apps-list.gen.h" +#include +#include +#include + +using namespace trygvis::apps; +using namespace std; + +const po::options_description logging_options() { + po::options_description desc; + + return desc; +} + +void setup_logging(po::variables_map vm) { + Appender *console = new ConsoleAppender(true, true); + PatternLayout *layout = new PatternLayout(LOG4CPLUS_TEXT("%-5p" /*" %6r"*/ " %-20c %m%n")); + console->setLayout(auto_ptr(layout)); + + Hierarchy &h = Logger::getDefaultHierarchy(); + h.getRoot().addAppender(SharedAppenderPtr(console)); +} + +template +int launch_app(int argc, const char *argv[]) { + App app; + + po::options_description all("Options"); + + auto all_options = all.add_options(); + app.add_options(all_options); + + all.add(logging_options()); + app.add_extra_options(all); + + po::variables_map vm; + + try { + auto parsed = po::parse_command_line(argc, argv, all); + po::store(parsed, vm); + + po::notify(vm); + + auto unrecognized = po::collect_unrecognized(parsed.options, po::include_positional); + + if (vm.count("help")) { + cerr << all << "\n"; + return EXIT_FAILURE; + } + + if (unrecognized.size()) { + cerr << "Unrecognized option: " << unrecognized.at(0) << "\n"; + return EXIT_FAILURE; + } + + setup_logging(vm); + + Logger logger = Logger::getInstance(LOG4CPLUS_TEXT(app.app_name)); + + app_execution execution(all, vm, logger); + + return app.main(execution); + } catch (po::required_option &e) { + cerr << "Missing required option: " << e.get_option_name() << endl; + cerr << all << endl; + return EXIT_FAILURE; + } catch (po::unknown_option &e) { + cerr << e.what() << endl; + return EXIT_FAILURE; + } +} + +int main(int argc, const char *argv[]) { + if (argc == 0) { + return EXIT_FAILURE; + } + + string app_name; + if (boost::ends_with(argv[0], "launcher")) { + if (argc <= 1) { + cerr << "Missing required argument: app" << endl; + return EXIT_FAILURE; + } + + app_name = argv[1]; + --argc; + argv = &argv[1]; + } else { + app_name = argv[0]; + } + + return launch(app_name, argc, argv); +} diff --git a/apps/log4cplus-test.cpp b/apps/log4cplus-test.cpp deleted file mode 100644 index 39d9884..0000000 --- a/apps/log4cplus-test.cpp +++ /dev/null @@ -1,50 +0,0 @@ -#include -#include -#include - -namespace trygvis { -using namespace log4cplus; -using namespace std; - -class LogSetup { -public: - LogSetup(string name) : logger(Logger::getInstance(LOG4CPLUS_TEXT(name))) { - } - -protected: - Logger logger; -}; - -class MyService : LogSetup { -public: - MyService() : LogSetup("trygvis.MyService") { - } - - void launchMissiles() { - LOG4CPLUS_DEBUG(logger, "some debug message"); - LOG4CPLUS_INFO(logger, "some info message"); - LOG4CPLUS_WARN(logger, "some warning message"); - } -}; -} - -using namespace std; -using namespace log4cplus; -using namespace trygvis; - -int main() { - BasicConfigurator config; - config.configure(); - - cout << "Hello world!" << endl; - - Logger l = Logger::getRoot(); - - l.setLogLevel(INFO_LOG_LEVEL); - - MyService myService; - - myService.launchMissiles(); - - return EXIT_SUCCESS; -} diff --git a/apps/sample-add-timestamp.cpp b/apps/sample-add-timestamp.cpp deleted file mode 100644 index 22d81a6..0000000 --- a/apps/sample-add-timestamp.cpp +++ /dev/null @@ -1,105 +0,0 @@ -#include "trygvis/sensor.h" -#include "trygvis/sensor/io.h" -#include "apps.h" -#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; - -class TimestampAddingSampleOutputStream : public SampleOutputStream { - -public: - TimestampAddingSampleOutputStream(shared_ptr output, KeyDictionary &dict, string timestamp_name) - : - timestamp_key(dict.indexOf(timestamp_name)) { - if (input_time_resolution_ == time_resolution::MILLISECONDS) { - factor = 1000; - } else { - factor = 1; - } - } - - virtual void write(SampleRecord const &sample) override { - time_t now = time(NULL) * factor; - - SampleRecord updated_sample(sample); - - updated_sample.set(timestamp_key, std::to_string(now)); - - output_->write(updated_sample); - }; - -private: - const SampleKey *timestamp_key; - time_resolution input_time_resolution_; - int factor; - shared_ptr output_; -}; - -class sample_add_timestamp : public app { - -private: - string timestamp_name; - time_resolution resolution; - sample_format_type output_format; - -public: - sample_add_timestamp() : app("sample-add-timestamp") { - } - ~sample_add_timestamp() = default; - - void add_options(po::options_description_easy_init &options) override { - options - ("help", "produce this help message") - ("resolution", po::value(&resolution)->default_value(time_resolution::SECONDS)) - ("timestamp-name", po::value(×tamp_name)->default_value("timestamp")) - ("output-format", po::value(&output_format)->default_value(sample_format_type::KEY_VALUE)); - } - - const int buffer_size = 1024; - - int main(app_execution &execution) override { - shared_ptr input; - - input = shared_ptr(&cin, noop_deleter); - - KeyDictionary dict; - - sample_output_stream_options options = {}; - unique_ptr unique_output_stream = open_sample_output_stream(shared_ptr(&cout, noop_deleter), dict, output_format, options); - shared_ptr output_stream{std::move(unique_output_stream)}; - shared_ptr p = make_shared(output_stream, dict, timestamp_name); - auto parser = open_sample_stream_parser(p, dict); - - 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_add_timestamp app; - return launch_app(argc, argv, app); -} diff --git a/apps/sample-add-timestamp.h b/apps/sample-add-timestamp.h new file mode 100644 index 0000000..13d2216 --- /dev/null +++ b/apps/sample-add-timestamp.h @@ -0,0 +1,99 @@ +#include "trygvis/sensor.h" +#include "trygvis/sensor/io.h" +#include "apps.h" +#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; + +class TimestampAddingSampleOutputStream : public SampleOutputStream { + +public: + TimestampAddingSampleOutputStream(shared_ptr output, KeyDictionary &dict, string timestamp_name) + : + timestamp_key(dict.indexOf(timestamp_name)) { + if (input_time_resolution_ == time_resolution::MILLISECONDS) { + factor = 1000; + } else { + factor = 1; + } + } + + virtual void write(SampleRecord const &sample) override { + time_t now = time(NULL) * factor; + + SampleRecord updated_sample(sample); + + updated_sample.set(timestamp_key, std::to_string(now)); + + output_->write(updated_sample); + }; + +private: + const SampleKey *timestamp_key; + time_resolution input_time_resolution_; + int factor; + shared_ptr output_; +}; + +class sample_add_timestamp : public app { + +private: + string timestamp_name; + time_resolution resolution; + sample_format_type output_format; + +public: + sample_add_timestamp() : app("sample-add-timestamp") { + } + + ~sample_add_timestamp() = default; + + void add_options(po::options_description_easy_init &options) override { + options + ("help", "produce this help message") + ("resolution", po::value(&resolution)->default_value(time_resolution::SECONDS)) + ("timestamp-name", po::value(×tamp_name)->default_value("timestamp")) + ("output-format", po::value(&output_format)->default_value(sample_format_type::KEY_VALUE)); + } + + const int buffer_size = 1024; + + int main(app_execution &execution) override { + shared_ptr input; + + input = shared_ptr(&cin, noop_deleter); + + KeyDictionary dict; + + sample_output_stream_options options = {}; + unique_ptr unique_output_stream = open_sample_output_stream(shared_ptr(&cout, noop_deleter), dict, output_format, options); + shared_ptr output_stream{std::move(unique_output_stream)}; + shared_ptr p = make_shared(output_stream, dict, timestamp_name); + auto parser = open_sample_stream_parser(p, dict); + + 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; + }; +}; + +} +} diff --git a/apps/sample-convert.cpp b/apps/sample-convert.cpp deleted file mode 100644 index 2dc34b3..0000000 --- a/apps/sample-convert.cpp +++ /dev/null @@ -1,128 +0,0 @@ -#include "trygvis/sensor.h" -#include "trygvis/sensor/io.h" -#include "json.hpp" -#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; -using boost::tokenizer; -namespace po = boost::program_options; - -class sample_convert : public app { -private: - string fields; - string timestamp_field; - bool add_timestamp; - string input_file, output_file; - sample_format_type output_format; - - string table_name; - -public: - sample_convert() : app("sample-convert") { - } - - ~sample_convert() = default; - - void add_options(po::options_description_easy_init &options) override { - options - ("help", "produce this help message") - ("input", po::value(&input_file)->default_value("-")) -// ("input-format", po::value(&input_format)->default_value("csv")) - ("output", po::value(&output_file)->default_value("-")) - ("output-format", po::value(&output_format)->default_value(sample_format_type::KEY_VALUE)) - ("fields", po::value(&fields)) - ("add-timestamp", po::value(&add_timestamp)->default_value(true)) - ("timestamp-field", po::value(×tamp_field)->default_value("timestamp")); - } - - void add_extra_options(po::options_description &all_options) override { - po::options_description sql("SQL"); - sql.add_options() - ("table-name", po::value(&table_name)); - - all_options.add(sql); - }; - - int main(app_execution &execution) override { - auto desc = execution.desc; - auto vm = execution.vm; - - KeyDictionary dict; - - istream *inputStream; - if (input_file == "-") { - inputStream = &cin; - } else { - inputStream = new ifstream(input_file); - if (inputStream->fail()) { - cerr << "Unable to open input file " << input_file << endl; - return EXIT_FAILURE; - } - } - - shared_ptr outputStream; - if (output_file == "-") { - outputStream = shared_ptr(&cout, noop_deleter); - } else { - outputStream = make_shared(output_file); - if (outputStream->fail()) { - cerr << "Unable to open output file " << output_file << endl; - return EXIT_FAILURE; - } - } - - sample_output_stream_options options; - trygvis::sensor::io::timestamp_field_option tf(timestamp_field); - - options.push_back(&tf); - - table_name_option tno(table_name); - if (table_name != "") { - options.push_back(&tno); - } - - tokenizer<> tok(fields); - output_fields_option fs; - std::copy(tok.begin(), tok.end(), std::back_inserter(fs.fields)); - if (!fs.fields.empty()) { - options.push_back(&fs); - } - - unique_ptr o = open_sample_output_stream(outputStream, dict, output_format, options); - - if (add_timestamp) { - o = make_unique(move(o), dict, timestamp_field); - } - - shared_ptr output(move(o)); - - auto input = make_shared(output, dict); - - char data[100]; - while (!inputStream->eof()) { - inputStream->get(data[0]); - auto buf = boost::asio::buffer(data, 1); - input->process(buf); - } - - 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/sample-convert.h b/apps/sample-convert.h new file mode 100644 index 0000000..0b805be --- /dev/null +++ b/apps/sample-convert.h @@ -0,0 +1,121 @@ +#include "trygvis/sensor.h" +#include "trygvis/sensor/io.h" +#include "json.hpp" +#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; +using boost::tokenizer; +namespace po = boost::program_options; + +class sample_convert : public app { +private: + string fields; + string timestamp_field; + bool add_timestamp; + string input_file, output_file; + sample_format_type output_format; + + string table_name; + +public: + sample_convert() : app("sample-convert") { + } + + ~sample_convert() = default; + + void add_options(po::options_description_easy_init &options) override { + options + ("help", "produce this help message") + ("input", po::value(&input_file)->default_value("-")) +// ("input-format", po::value(&input_format)->default_value("csv")) + ("output", po::value(&output_file)->default_value("-")) + ("output-format", po::value(&output_format)->default_value(sample_format_type::KEY_VALUE)) + ("fields", po::value(&fields)) + ("add-timestamp", po::value(&add_timestamp)->default_value(true)) + ("timestamp-field", po::value(×tamp_field)->default_value("timestamp")); + } + + void add_extra_options(po::options_description &all_options) override { + po::options_description sql("SQL"); + sql.add_options() + ("table-name", po::value(&table_name)); + + all_options.add(sql); + }; + + int main(app_execution &execution) override { + auto desc = execution.desc; + auto vm = execution.vm; + + KeyDictionary dict; + + istream *inputStream; + if (input_file == "-") { + inputStream = &cin; + } else { + inputStream = new ifstream(input_file); + if (inputStream->fail()) { + cerr << "Unable to open input file " << input_file << endl; + return EXIT_FAILURE; + } + } + + shared_ptr outputStream; + if (output_file == "-") { + outputStream = shared_ptr(&cout, noop_deleter); + } else { + outputStream = make_shared(output_file); + if (outputStream->fail()) { + cerr << "Unable to open output file " << output_file << endl; + return EXIT_FAILURE; + } + } + + sample_output_stream_options options; + trygvis::sensor::io::timestamp_field_option tf(timestamp_field); + + options.push_back(&tf); + + table_name_option tno(table_name); + if (table_name != "") { + options.push_back(&tno); + } + + tokenizer<> tok(fields); + output_fields_option fs; + std::copy(tok.begin(), tok.end(), std::back_inserter(fs.fields)); + if (!fs.fields.empty()) { + options.push_back(&fs); + } + + unique_ptr o = open_sample_output_stream(outputStream, dict, output_format, options); + + if (add_timestamp) { + o = make_unique(move(o), dict, timestamp_field); + } + + shared_ptr output(move(o)); + + auto input = make_shared(output, dict); + + char data[100]; + while (!inputStream->eof()) { + inputStream->get(data[0]); + auto buf = boost::asio::buffer(data, 1); + input->process(buf); + } + + return EXIT_SUCCESS; + } +}; + +} +} diff --git a/apps/sample-select.cpp b/apps/sample-select.cpp deleted file mode 100644 index c4304da..0000000 --- a/apps/sample-select.cpp +++ /dev/null @@ -1,128 +0,0 @@ -#include "trygvis/sensor.h" -#include "apps.h" -#include - -namespace trygvis { -namespace apps { - -using namespace std; -using namespace trygvis::apps; -using namespace trygvis::sensor; -using boost::tokenizer; -namespace po = boost::program_options; - -class sample_select : public app { - -private: - string fields; - -public: - sample_select() : app("sample-select") { - } - - ~sample_select() = default; - - void add_options(po::options_description_easy_init &options) override { - options - ("help", "produce this help message") - ("fields", po::value(&fields)->required()); - } - - int main(app_execution &execution) override { - tokenizer<> tok(fields); - - for (tokenizer<>::iterator beg = tok.begin(); beg != tok.end(); ++beg) { - cout << *beg << "\n"; - } - - /* - KeyDictionary dict; - - relative_key = dict.indexOf(relative_name); - - auto sample_buffer = make_shared(); - auto parser = open_sample_input_stream(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_select app; - return launch_app(argc, argv, app); -} diff --git a/apps/sample-select.h b/apps/sample-select.h new file mode 100644 index 0000000..2701338 --- /dev/null +++ b/apps/sample-select.h @@ -0,0 +1,121 @@ +#include "trygvis/sensor.h" +#include "apps.h" +#include + +namespace trygvis { +namespace apps { + +using namespace std; +using namespace trygvis::apps; +using namespace trygvis::sensor; +using boost::tokenizer; +namespace po = boost::program_options; + +class sample_select : public app { + +private: + string fields; + +public: + sample_select() : app("sample-select") { + } + + ~sample_select() = default; + + void add_options(po::options_description_easy_init &options) override { + options + ("help", "produce this help message") + ("fields", po::value(&fields)->required()); + } + + int main(app_execution &execution) override { + tokenizer<> tok(fields); + + for (tokenizer<>::iterator beg = tok.begin(); beg != tok.end(); ++beg) { + cout << *beg << "\n"; + } + + /* + KeyDictionary dict; + + relative_key = dict.indexOf(relative_name); + + auto sample_buffer = make_shared(); + auto parser = open_sample_input_stream(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; + }; +}; + +} +} diff --git a/apps/sm-db-insert.cpp b/apps/sm-db-insert.cpp deleted file mode 100644 index a3ebb76..0000000 --- a/apps/sm-db-insert.cpp +++ /dev/null @@ -1,78 +0,0 @@ -#include -#include -#include -#include -#include "json.hpp" -#include - -template -using o = boost::optional; -using namespace std; -using json = nlohmann::json; - -class missing_key : public runtime_error { -public: - missing_key(const json::string_t key) : runtime_error("Missing key: " + key), key(key) { - } - - const json::string_t key; -}; - -template -T get(json j, string key) { - auto ref = j[key]; - - if (ref.is_null()) { - throw missing_key(key); - } - - return ref; -} - -int main(int argc, char *argv[]) { - cout << "reading from " << argv[1] << endl; - - fstream f(argv[1]); - - json j; - - j << f; - - pqxx::connection c("host=localhost dbname=soil-moisture"); - - pqxx::work work(c); - - string mac = j["mac"]; // "aa:bb:cc:dd:ee:ff"; - - auto rs = work.parameterized("select id from soil_moisture_device where mac=$1")(mac).exec(); - - if (!rs.size()) { - cout << "New device: " << mac << endl; - - rs = work.parameterized("insert into soil_moisture_device(mac) values($1) returning id")(mac).exec(); - } - - auto deviceId = rs.begin()["id"].as(); - - int sensor = j["sensor"]; - - rs = work.parameterized("select id from soil_moisture_sensor where device=$1 and sensor=$2")(deviceId)(sensor).exec(); - - if (!rs.size()) { - cout << "New sensor: " << sensor << endl; - - rs = work.parameterized("insert into soil_moisture_sensor(device, sensor) values($1, $2) returning id")(deviceId)(sensor).exec(); - } - auto sensorId = rs.begin()["id"].as(); - - unsigned long timestamp = get(j, "timestamp"); - unsigned int value = get(j, "value"); - - work.parameterized("insert into soil_moisture_sample(sensor, timestamp, value) values($1, $2, $3)")(sensorId)(timestamp)(value).exec(); - - cout << "Sample inserted" << endl; - - work.commit(); - - return EXIT_SUCCESS; -} diff --git a/apps/sm-db-insert.h b/apps/sm-db-insert.h new file mode 100644 index 0000000..b786e41 --- /dev/null +++ b/apps/sm-db-insert.h @@ -0,0 +1,80 @@ +#include +#include +#include +#include +#include "json.hpp" +#include + +namespace trygvis { +namespace apps { + +template +using o = boost::optional; +using namespace std; +using json = nlohmann::json; + +class sm_db_insert : public app { + +public: + sm_db_insert() : app("sm-db-insert") { + } + + ~sm_db_insert() = default; + + void add_options(po::options_description_easy_init &options) override { + options + ("file", po::value()->required(), "The file to read"); + } + + int main(app_execution &execution) override { + auto file = execution.vm["file"].as(); + cout << "reading from " << file << endl; + + fstream f(file); + + json j; + + j << f; + + pqxx::connection c("host=localhost dbname=soil-moisture"); + + pqxx::work work(c); + + string mac = j["mac"]; // "aa:bb:cc:dd:ee:ff"; + + auto rs = work.parameterized("select id from soil_moisture_device where mac=$1")(mac).exec(); + + if (!rs.size()) { + cout << "New device: " << mac << endl; + + rs = work.parameterized("insert into soil_moisture_device(mac) values($1) returning id")(mac).exec(); + } + + auto deviceId = rs.begin()["id"].as(); + + int sensor = j["sensor"]; + + rs = work.parameterized("select id from soil_moisture_sensor where device=$1 and sensor=$2")(deviceId)(sensor).exec(); + + if (!rs.size()) { + cout << "New sensor: " << sensor << endl; + + rs = work.parameterized("insert into soil_moisture_sensor(device, sensor) values($1, $2) returning id")(deviceId)(sensor).exec(); + } + auto sensorId = rs.begin()["id"].as(); + + unsigned long timestamp = get(j, "timestamp"); + unsigned int value = get(j, "value"); + + work.parameterized("insert into soil_moisture_sample(sensor, timestamp, value) values($1, $2, $3)")(sensorId)(timestamp)(value).exec(); + + cout << "Sample inserted" << endl; + + work.commit(); + + return EXIT_SUCCESS; + } +}; + +} +} diff --git a/apps/sm-db-select.cpp b/apps/sm-db-select.cpp deleted file mode 100644 index 8bcce32..0000000 --- a/apps/sm-db-select.cpp +++ /dev/null @@ -1,82 +0,0 @@ -#include -#include -#include -#include -#include "json.hpp" - -template -using o = boost::optional; -using namespace std; -using json = nlohmann::json; - -class missing_key : public runtime_error { -public: - missing_key(const json::string_t key) : runtime_error("Missing key: " + key), key(key) { - } - - const json::string_t key; -}; - -template -T get(json j, string key) { - auto ref = j[key]; - - if (ref.is_null()) { - throw missing_key(key); - } - - return ref; -} - -int main(int argc, char *argv[]) { - string mac = "aa:bb:cc:dd:ee:ff"; - int sensor = 1; - - json j; - - pqxx::connection c("host=localhost dbname=soil-moisture"); - - pqxx::work work(c); - - auto rs = work.parameterized("select id from soil_moisture_device where mac=$1")(mac).exec(); - - if (!rs.size()) { - cout << "Unknown device: " << mac << endl; - return EXIT_FAILURE; - } - - auto deviceId = rs.begin()["id"].as(); - - rs = work.parameterized("select id from soil_moisture_sensor where device=$1 and sensor=$2")(deviceId)(sensor).exec(); - - if (!rs.size()) { - cout << "Unknown sensor: " << sensor << endl; - return EXIT_FAILURE; - } - - auto sensorId = rs.begin()["id"].as(); - - rs = work.parameterized("select timestamp, sensor, value from soil_moisture_sample where sensor=$1")(sensorId).exec(); - - json points = json::array(); - for (auto sample: rs) { - json s; - s.push_back(sample["timestamp"].as()); - s.push_back(sample["sensor"].as()); - s.push_back(sample["value"].as()); - points.push_back({s}); - } - - json o; - o["columns"] = json::array({"time", "sensor", "value"}); - o["points"] = json(points); - j.push_back(o); - - cout << "JSON" << endl; - cout << setw(2) << j << endl; - cout << "JSON" << endl; - - work.commit(); - - return EXIT_SUCCESS; -} diff --git a/apps/sm-db-select.h b/apps/sm-db-select.h new file mode 100644 index 0000000..fcbe91b --- /dev/null +++ b/apps/sm-db-select.h @@ -0,0 +1,81 @@ +#include +#include +#include +#include +#include "json.hpp" + +namespace trygvis { +namespace apps { + +template +using o = boost::optional; +using namespace std; +using json = nlohmann::json; + +class sm_db_select : public app { + +public: + sm_db_select() : app("sm-db-select") { + } + + ~sm_db_select() = default; + + int main(app_execution &execution) override { + string mac = "aa:bb:cc:dd:ee:ff"; + int sensor = 1; + + json j; + + pqxx::connection c("host=localhost dbname=soil-moisture"); + + pqxx::work work(c); + + auto rs = work.parameterized("select id from soil_moisture_device where mac=$1")(mac).exec(); + + if (!rs.size()) { + cout << "Unknown device: " << mac << endl; + return EXIT_FAILURE; + } + + auto deviceId = rs.begin()["id"].as(); + + rs = work.parameterized("select id from soil_moisture_sensor where device=$1 and sensor=$2")(deviceId)( + sensor).exec(); + + if (!rs.size()) { + cout << "Unknown sensor: " << sensor << endl; + return EXIT_FAILURE; + } + + auto sensorId = rs.begin()["id"].as(); + + rs = work.parameterized("select timestamp, sensor, value from soil_moisture_sample where sensor=$1")( + sensorId).exec(); + + json points = json::array(); + for (auto sample: rs) { + json s; + s.push_back(sample["timestamp"].as()); + s.push_back(sample["sensor"].as()); + s.push_back(sample["value"].as()); + points.push_back({s}); + } + + json o; + o["columns"] = json::array({"time", "sensor", "value"}); + o["points"] = json(points); + j.push_back(o); + + cout << "JSON" << endl; + cout << setw(2) << j << endl; + cout << "JSON" << endl; + + work.commit(); + + return EXIT_SUCCESS; + } + +}; + +} +} diff --git a/apps/sm-get-value.cpp b/apps/sm-get-value.cpp deleted file mode 100644 index 883495a..0000000 --- a/apps/sm-get-value.cpp +++ /dev/null @@ -1,159 +0,0 @@ -#include -#include -#include -#include -#include -#include "ble/Bluetooth.h" -#include "SoilMoisture.h" -#include "trygvis/sensor.h" -#include "json.hpp" -#include "apps.h" - -// I'm lazy -using namespace std; -using namespace std::chrono; -using namespace trygvis::apps; -using namespace trygvis::bluetooth; -using namespace trygvis::sensor; -namespace po = boost::program_options; -using json = nlohmann::json; - -bool loop; -sample_format_type format; -time_point targetTime; -unsigned int sleepTime; -vector sensors; - -void withConnection(BluetoothGatt &gatt) { - SoilMoisture soilMoisture = SoilMoisture::create(gatt); - - const int sensorCount = soilMoisture.getSensorCount(); - - if (sensorCount == 0) { - throw runtime_error("Sensor count is 0"); - } - - // If the user didn't specify any sensors, add all. - if (sensors.size() == 0) { - for (int i = 0; i < sensorCount; i++) { - sensors.push_back(i); - } - } - - auto device = gatt.getDevice().getMac(); - - targetTime = system_clock::now(); - - do { - for (auto sensor : sensors) { - if (sensor >= sensorCount) { - // Ignore invalid sensors - continue; - } - - auto epoch = system_clock::now().time_since_epoch(); - auto timestamp = duration_cast(epoch).count(); - uint16_t value = soilMoisture.getValue((uint8_t) sensor); - - 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 == 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 == sample_format_type::SQL) { - cout << "INSERT INTO soil_moisture_sample(device, sensor, timestamp, value) VALUES(" - << "'" << device.str() << "', " - << sensor << ", " - << timestamp << ", " - << value << ";" << endl; - } - } - - targetTime = targetTime + seconds(sleepTime); - this_thread::sleep_until(targetTime); - } while (loop); -} - -namespace trygvis { -namespace apps { - -class sm_get_value : public app { - -public: - sm_get_value() : app("sm-get-value") { - } - - ~sm_get_value() = default; - - void add_options(po::options_description_easy_init &options) override { - auto default_sleep = po::value<>(&sleepTime)->default_value(0); - - options("help", "produce help message") - ("device", po::value()->required(), "MAC of device to poll") - ("sensor", po::value>(&sensors)->multitoken(), "Sensor to poll, defaults to all") - ("sleep", default_sleep, "How long to sleep in seconds between each poll. If not given, it will exit after first poll") - ("format", po::value(&format)->default_value(sample_format_type::KEY_VALUE), - "Output format"); - } - - int main(app_execution &execution) override { - __attribute__((unused)) - BluetoothSystem bluetoothSystem; - - auto desc = execution.desc; - auto vm = execution.vm; - - try { - if (!vm.count("device")) { - cerr << "Missing required option: device" << endl; - cerr << desc << "\n"; - return EXIT_FAILURE; - } - - auto MAC = vm["device"].as(); - - Mac mac = Mac::parseMac(MAC); - - auto &adapter = getAdapter(0); - - auto &device = adapter.getDevice(mac); - - loop = sleepTime > 0; - - do { - LOG4CPLUS_INFO(logger, "Connecting to device: " + device.getMac().str()); - - auto &gatt = device.connectGatt(); - try { - withConnection(gatt); - } catch (runtime_error &e) { - cout << "exception: " << e.what() << endl; - } - gatt.disconnect(); - } while (loop); - - return EXIT_SUCCESS; - } catch (std::runtime_error ex) { - cout << "std::runtime_error: " << ex.what() << endl; - return EXIT_FAILURE; - } catch (std::exception ex) { - cout << "std::exception: " << ex.what() << endl; - return EXIT_FAILURE; - } - } -}; - -} -} - -int main(int argc, char *argv[]) { - sm_get_value app; - return trygvis::apps::launch_app(argc, argv, app); -} diff --git a/apps/sm-get-value.h b/apps/sm-get-value.h new file mode 100644 index 0000000..e0275c1 --- /dev/null +++ b/apps/sm-get-value.h @@ -0,0 +1,153 @@ +#include +#include +#include +#include +#include +#include "ble/Bluetooth.h" +#include "SoilMoisture.h" +#include "trygvis/sensor.h" +#include "json.hpp" +#include "apps.h" + +// I'm lazy +using namespace std; +using namespace std::chrono; +using namespace trygvis::apps; +using namespace trygvis::bluetooth; +using namespace trygvis::sensor; +using json = nlohmann::json; + +bool loop; +sample_format_type format; +time_point targetTime; +unsigned int sleepTime; +vector sensors; + +void withConnection(BluetoothGatt &gatt) { + SoilMoisture soilMoisture = SoilMoisture::create(gatt); + + const int sensorCount = soilMoisture.getSensorCount(); + + if (sensorCount == 0) { + throw runtime_error("Sensor count is 0"); + } + + // If the user didn't specify any sensors, add all. + if (sensors.size() == 0) { + for (int i = 0; i < sensorCount; i++) { + sensors.push_back(i); + } + } + + auto device = gatt.getDevice().getMac(); + + targetTime = system_clock::now(); + + do { + for (auto sensor : sensors) { + if (sensor >= sensorCount) { + // Ignore invalid sensors + continue; + } + + auto epoch = system_clock::now().time_since_epoch(); + auto timestamp = duration_cast(epoch).count(); + uint16_t value = soilMoisture.getValue((uint8_t) sensor); + + 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 == 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 == sample_format_type::SQL) { + cout << "INSERT INTO soil_moisture_sample(device, sensor, timestamp, value) VALUES(" + << "'" << device.str() << "', " + << sensor << ", " + << timestamp << ", " + << value << ";" << endl; + } + } + + targetTime = targetTime + seconds(sleepTime); + this_thread::sleep_until(targetTime); + } while (loop); +} + +namespace trygvis { +namespace apps { + +class sm_get_value : public app { + +public: + sm_get_value() : app("sm-get-value") { + } + + ~sm_get_value() = default; + + void add_options(po::options_description_easy_init &options) override { + auto default_sleep = po::value<>(&sleepTime)->default_value(0); + + options("help", "produce help message") + ("device", po::value()->required(), "MAC of device to poll") + ("sensor", po::value>(&sensors)->multitoken(), "Sensor to poll, defaults to all") + ("sleep", default_sleep, "How long to sleep in seconds between each poll. If not given, it will exit after first poll") + ("format", po::value(&format)->default_value(sample_format_type::KEY_VALUE), + "Output format"); + } + + int main(app_execution &execution) override { + __attribute__((unused)) + BluetoothSystem bluetoothSystem; + + auto desc = execution.desc; + auto vm = execution.vm; + + try { + if (!vm.count("device")) { + cerr << "Missing required option: device" << endl; + cerr << desc << "\n"; + return EXIT_FAILURE; + } + + auto MAC = vm["device"].as(); + + Mac mac = Mac::parseMac(MAC); + + auto &adapter = getAdapter(0); + + auto &device = adapter.getDevice(mac); + + loop = sleepTime > 0; + + do { + LOG4CPLUS_INFO(execution.logger, "Connecting to device: " + device.getMac().str()); + + auto &gatt = device.connectGatt(); + try { + withConnection(gatt); + } catch (runtime_error &e) { + cout << "exception: " << e.what() << endl; + } + gatt.disconnect(); + } while (loop); + + return EXIT_SUCCESS; + } catch (std::runtime_error ex) { + cout << "std::runtime_error: " << ex.what() << endl; + return EXIT_FAILURE; + } catch (std::exception ex) { + cout << "std::exception: " << ex.what() << endl; + return EXIT_FAILURE; + } + } +}; + +} +} diff --git a/apps/sm-serial-read-all.cpp b/apps/sm-serial-read-all.cpp deleted file mode 100644 index 5fa3f79..0000000 --- a/apps/sm-serial-read-all.cpp +++ /dev/null @@ -1,175 +0,0 @@ -#include "trygvis/sensor.h" -#include "trygvis/sensor/io.h" -#include "json.hpp" -#include "apps.h" -#include -#include - -namespace trygvis { -namespace apps { - -using namespace boost::asio; -using namespace std; -using namespace std::chrono; -using namespace trygvis::apps; -using namespace trygvis::sensor; -using namespace trygvis::sensor::io; -namespace po = boost::program_options; -using json = nlohmann::json; - -sample_format_type format; -string hostname = get_hostname(); - -class port_handler { -public: - port_handler(string &port_name, serial_port &port, unique_ptr parser) : - port_name(port_name), port(port), parser(move(parser)) { - } - - void run() { - auto packet = make_shared>(1024); - - while (port.is_open()) { - std::size_t some = port.read_some(buffer); - - mutable_buffers_1 chunk = boost::asio::buffer(data, some); - parser->process(chunk); - } - - cerr << "port closed" << endl; - dead_ = true; - } - - bool dead() const { - return dead_; - } - - const string port_name; - -private: - static const size_t size = 1024; - bool dead_ = false; - serial_port &port; - uint8_t data[size]; - mutable_buffers_1 buffer = boost::asio::buffer(data, size); - - unique_ptr parser; -}; - -// This only supports Linux -#if 1 - -#include -#include - -vector &&find_ports() { - DIR *dir = opendir("/dev"); - vector ports; - - if (!dir) { - return move(ports); - } - - struct dirent *entry; - while ((entry = readdir(dir)) != NULL) { - string name = entry->d_name; - - if (name.find("ttyS") || name.find("ttyUSB") || name.find("ttyACM")) { - ports.emplace_back("/dev/" + name); - } - } - - closedir(dir); - - return move(ports); -} - -#endif - -class sm_serial_read_all : public app { - -public: - sm_serial_read_all() : app("sm-serial-read-all") { - } - - ~sm_serial_read_all() = default; - - bool run = true; - - void add_options(po::options_description_easy_init &options) override { - options - ("help", "produce help message") - ("port", po::value()->required(), "The serial port to read") - ("format", po::value(&format)->default_value(sample_format_type::KEY_VALUE), "Output format"); - } - - static - void handler_thread(port_handler *handler) { - handler->run(); - } - - int main(app_execution &execution) override { - auto desc = execution.desc; - auto vm = execution.vm; - - io_service io_service; - uint32_t baud_rate = 115200; - - map active_ports; - - KeyDictionary outputDict; - auto output_stream = shared_ptr(&cout, noop_deleter); - auto output = open_sample_output_stream(output_stream, outputDict, format); - auto tso = thread_safe_sample_output_stream(move(output)); - shared_ptr thread_safe_output(move(tso)); - - while (run) { - // Port cleanup - for (auto it = active_ports.begin(); it != active_ports.end(); ++it) { - if (it->second->dead()) { - cerr << "Removing dead port " << it->second->port_name << endl; - it = active_ports.erase(it); - } - } - - // Discover new ports - auto ports = find_ports(); - - for (auto port_name : ports) { - if (active_ports.find(port_name) != active_ports.end()) { - cerr << "New port " << 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)); - - KeyDictionary parserDict; // TODO: dette feiler - unique_ptr parser = open_sample_stream_parser(thread_safe_output, parserDict, sample_format_type::KEY_VALUE); - - auto handler = new port_handler(port_name, port, move(parser)); - active_ports[port_name] = handler; - std::thread thread(handler_thread, handler); - } - } - - sleep(1); - } - - return EXIT_SUCCESS; - } - -}; - -} -} - -using namespace trygvis::apps; - -int main(int argc, char *argv[]) { - sm_serial_read_all app; - return launch_app(argc, argv, app); -} diff --git a/apps/sm-serial-read-all.h b/apps/sm-serial-read-all.h new file mode 100644 index 0000000..39b585a --- /dev/null +++ b/apps/sm-serial-read-all.h @@ -0,0 +1,171 @@ +#include "trygvis/sensor.h" +#include "trygvis/sensor/io.h" +#include "json.hpp" +#include "apps.h" +#include +#include + +namespace trygvis { +namespace apps { + +using namespace boost::asio; +using namespace std; +using namespace std::chrono; +using namespace trygvis::apps; +using namespace trygvis::sensor; +using namespace trygvis::sensor::io; +namespace po = boost::program_options; +using json = nlohmann::json; + +namespace sm_serial_read_all_utils { + +class port_handler { +public: + port_handler(string &port_name, serial_port &port, unique_ptr parser) : + port_name(port_name), port(port), parser(move(parser)) { + } + + void run() { + auto packet = make_shared>(1024); + + while (port.is_open()) { + std::size_t some = port.read_some(buffer); + + mutable_buffers_1 chunk = boost::asio::buffer(data, some); + parser->process(chunk); + } + + cerr << "port closed" << endl; + dead_ = true; + } + + bool dead() const { + return dead_; + } + + const string port_name; + +private: + static const size_t size = 1024; + bool dead_ = false; + serial_port &port; + uint8_t data[size]; + mutable_buffers_1 buffer = boost::asio::buffer(data, size); + + unique_ptr parser; +}; + +} + +// This only supports Linux +#if 1 + +#include +#include + +vector &&find_ports() { + DIR *dir = opendir("/dev"); + vector ports; + + if (!dir) { + return move(ports); + } + + struct dirent *entry; + while ((entry = readdir(dir)) != NULL) { + string name = entry->d_name; + + if (name.find("ttyS") || name.find("ttyUSB") || name.find("ttyACM")) { + ports.emplace_back("/dev/" + name); + } + } + + closedir(dir); + + return move(ports); +} + +#endif + +class sm_serial_read_all : public app { + typedef trygvis::apps::sm_serial_read_all_utils::port_handler port_handler; + +public: + sm_serial_read_all() : app("sm-serial-read-all") { + } + + ~sm_serial_read_all() = default; + + bool run = true; + sample_format_type format; + + void add_options(po::options_description_easy_init &options) override { + options + ("help", "produce help message") + ("port", po::value()->required(), "The serial port to read") + ("format", po::value(&format)->default_value(sample_format_type::KEY_VALUE), "Output format"); + } + + static + void handler_thread(port_handler *handler) { + handler->run(); + } + + int main(app_execution &execution) override { + auto desc = execution.desc; + auto vm = execution.vm; + + io_service io_service; + uint32_t baud_rate = 115200; + + map active_ports; + + KeyDictionary outputDict; + auto output_stream = shared_ptr(&cout, noop_deleter); + auto output = open_sample_output_stream(output_stream, outputDict, format); + auto tso = thread_safe_sample_output_stream(move(output)); + shared_ptr thread_safe_output(move(tso)); + + while (run) { + // Port cleanup + for (auto it = active_ports.begin(); it != active_ports.end(); ++it) { + if (it->second->dead()) { + cerr << "Removing dead port " << it->second->port_name << endl; + it = active_ports.erase(it); + } + } + + // Discover new ports + auto ports = find_ports(); + + for (auto port_name : ports) { + if (active_ports.find(port_name) != active_ports.end()) { + cerr << "New port " << 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)); + + KeyDictionary parserDict; // TODO: dette feiler + unique_ptr parser = open_sample_stream_parser(thread_safe_output, parserDict, sample_format_type::KEY_VALUE); + + auto handler = new port_handler(port_name, port, move(parser)); + active_ports[port_name] = handler; + std::thread thread(handler_thread, handler); + } + } + + sleep(1); + } + + return EXIT_SUCCESS; + } + +}; + +} +} diff --git a/apps/sm-serial-read.cpp b/apps/sm-serial-read.cpp deleted file mode 100644 index 8c4c299..0000000 --- a/apps/sm-serial-read.cpp +++ /dev/null @@ -1,114 +0,0 @@ -#include "trygvis/sensor.h" -#include "trygvis/sensor/io.h" -#include "json.hpp" -#include "apps.h" -#include -#include -#include - -namespace trygvis { -namespace apps { - -using namespace boost::asio; -using namespace std; -using namespace std::chrono; -using namespace trygvis::apps; -using namespace trygvis::sensor; -using namespace trygvis::sensor::io; -namespace po = boost::program_options; -using json = nlohmann::json; - -sample_format_type format; -string hostname = get_hostname(); - -class port_handler { -public: - port_handler(string port_name, serial_port &serial_port, shared_ptr input) : - port_name(port_name), port(serial_port), input(input) { - } - - void run() { - auto packet = make_shared>(1024); - - while (port.is_open()) { - std::size_t some = port.read_some(buffer); - - mutable_buffers_1 chunk = boost::asio::buffer(data, some); - input->process(chunk); - } - - cerr << "port closed" << endl; - } - - -private: - static const size_t size = 1024; - string port_name; - serial_port &port; - uint8_t data[size]; - mutable_buffers_1 buffer = boost::asio::buffer(data, size); - - shared_ptr input; -}; - -class sm_serial_read : public app { - -public: - sm_serial_read() : app("sm-serial-read") { - } - - ~sm_serial_read() = default; - - void add_options(po::options_description_easy_init &options) override { - options - ("help", "produce help message") - ("port", po::value()->required(), "The serial port to read") - ("format", po::value(&format)->default_value(sample_format_type::KEY_VALUE), "Output format"); - } - - int main(app_execution &execution) override { - auto desc = execution.desc; - auto vm = execution.vm; - - KeyDictionary dict; - - uint32_t baud_rate = 115200; - auto port_name = vm["port"].as(); - - io_service io_service; - - 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)); - - if (port.is_open()) { - cerr << "port is open" << endl; - } else { - cerr << "port is not open" << endl; - } - - shared_ptr outputStream = shared_ptr(&cout, noop_deleter); - shared_ptr output = open_sample_output_stream(outputStream, dict, format); - - shared_ptr input = make_shared(output, dict); - - port_handler(port_name, port, input).run(); - - return EXIT_SUCCESS; - } - -}; - -} -} - -using namespace trygvis::apps; - -int main(int argc, char *argv[]) { - sm_serial_read app; - return launch_app(argc, argv, app); -} diff --git a/apps/sm-serial-read.h b/apps/sm-serial-read.h new file mode 100644 index 0000000..a3e3872 --- /dev/null +++ b/apps/sm-serial-read.h @@ -0,0 +1,111 @@ +#include "trygvis/sensor.h" +#include "trygvis/sensor/io.h" +#include "json.hpp" +#include "apps.h" +#include +#include +#include + +namespace trygvis { +namespace apps { + +using namespace boost::asio; +using namespace std; +using namespace std::chrono; +using namespace trygvis::apps; +using namespace trygvis::sensor; +using namespace trygvis::sensor::io; +namespace po = boost::program_options; +using json = nlohmann::json; + +namespace sm_serial_read_utils { + +class port_handler { +public: + port_handler(string port_name, serial_port &serial_port, shared_ptr input) : + port_name(port_name), port(serial_port), input(input) { + } + + void run() { + auto packet = make_shared>(1024); + + while (port.is_open()) { + std::size_t some = port.read_some(buffer); + + mutable_buffers_1 chunk = boost::asio::buffer(data, some); + input->process(chunk); + } + + cerr << "port closed" << endl; + } + + +private: + static const size_t size = 1024; + string port_name; + serial_port &port; + uint8_t data[size]; + mutable_buffers_1 buffer = boost::asio::buffer(data, size); + + shared_ptr input; +}; + +} + +class sm_serial_read : public app { + typedef trygvis::apps::sm_serial_read_utils::port_handler port_handler; + +public: + sm_serial_read() : app("sm-serial-read") { + } + + ~sm_serial_read() = default; + + sample_format_type format; + + void add_options(po::options_description_easy_init &options) override { + options + ("help", "produce help message") + ("port", po::value()->required(), "The serial port to read") + ("format", po::value(&format)->default_value(sample_format_type::KEY_VALUE), "Output format"); + } + + int main(app_execution &execution) override { + auto desc = execution.desc; + auto vm = execution.vm; + + KeyDictionary dict; + + uint32_t baud_rate = 115200; + auto port_name = vm["port"].as(); + + io_service io_service; + + 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)); + + if (port.is_open()) { + cerr << "port is open" << endl; + } else { + cerr << "port is not open" << endl; + } + + shared_ptr outputStream = shared_ptr(&cout, noop_deleter); + shared_ptr output = open_sample_output_stream(outputStream, dict, format); + + shared_ptr input = make_shared(output, dict); + + port_handler(port_name, port, input).run(); + + return EXIT_SUCCESS; + } + +}; + +} +} -- cgit v1.2.3