From 2ca532122d60cff4dbdc7f24fbc5783bcc5ad68d Mon Sep 17 00:00:00 2001 From: Trygve Laugstøl Date: Tue, 12 Apr 2016 20:06:47 +0200 Subject: Soil Moisture: Adding support for controlling lights. Bluetooth: refectorying, trying to be more c++ idiomatic and modern. SM/Diller: adding bluetooth to Diller bridge. --- apps/CMakeLists.txt | 80 ++++--- apps/SoilMoisture.cpp | 50 +++-- apps/SoilMoisture.h | 26 ++- apps/apps.cpp | 2 +- apps/apps.h | 69 +++++- apps/ble-inspect-device.cpp | 8 +- apps/ble-scan.cpp | 4 +- apps/diller_util.cpp | 19 ++ apps/diller_util.h | 14 ++ apps/mqtt_support.h | 72 +++++-- apps/sm-diller.cpp | 423 +++++++++++++++++++++++++++++++++++++ apps/sm-get-value.cpp | 12 +- ble/BluetoothImpl.h | 131 +++++++----- ble/LinuxBluetooth.cpp | 31 ++- include/ble/Bluetooth.h | 49 +++-- json | 2 +- sensor/include/trygvis/sensor.h | 43 ++-- sensor/include/trygvis/sensor/io.h | 2 +- sensor/main/io.cpp | 30 +-- sensor/main/sensor.cpp | 2 +- sensor/test/SampleTest.cpp | 16 +- test/ByteBufferTest.cpp | 2 +- 22 files changed, 866 insertions(+), 221 deletions(-) create mode 100644 apps/diller_util.cpp create mode 100644 apps/diller_util.h create mode 100644 apps/sm-diller.cpp diff --git a/apps/CMakeLists.txt b/apps/CMakeLists.txt index 34fbd30..98c9915 100644 --- a/apps/CMakeLists.txt +++ b/apps/CMakeLists.txt @@ -1,21 +1,3 @@ -list(APPEND APPS ble-inspect-device) -list(APPEND APPS ble-scan) -list(APPEND APPS sample-add-timestamp) -list(APPEND APPS sample-convert) -list(APPEND APPS sample-select) -list(APPEND APPS sm-db-insert) -list(APPEND APPS sm-db-select) -list(APPEND APPS sm-get-value) -list(APPEND APPS sm-serial-read) -list(APPEND APPS sm-serial-read-all) - -list(APPEND INCLUDE_DIRECTORIES - ${CMAKE_CURRENT_SOURCE_DIR} - "${PROJECT_SOURCE_DIR}/include" - "${PROJECT_SOURCE_DIR}/json/src" - "${PROJECT_SOURCE_DIR}/gsl/include" - "${PROJECT_SOURCE_DIR}/sensor/include") - #### Find all packages function(find_header_and_lib PREFIX HEADER_NAME LIBRARY_NAME) @@ -70,8 +52,6 @@ list(APPEND LIBRARIES "${LOG4CPLUS_LIBRARY}") find_header_and_lib(MOSQUITTO mosquitto.h mosquitto) if(MOSQUITTO_OK STREQUAL "OK") - list(APPEND APPS mqtt-publish) - set(mqtt-publish_SOURCES mqtt_support.cpp mqtt_support.h) list(APPEND INCLUDE_DIRECTORIES "${MOSQUITTO_INCLUDE_DIRECTORY}") list(APPEND LIBRARIES "${MOSQUITTO_LIBRARY}") else() @@ -81,26 +61,56 @@ endif() message(STATUS "Dependency summary:") message(STATUS "Log4cplus: -I${LOG4CPLUS_INCLUDE_DIRECTORY}, -L${LOG4CPLUS_LIBRARY}") message(STATUS "Mosquitto: -I${MOSQUITTO_INCLUDE_DIRECTORY}, -L${MOSQUITTO_LIBRARY}") -message(STATUS "Mosquittopp: -I${MOSQUITTOPP_INCLUDE_DIRECTORY}, -L${MOSQUITTOPP_LIBRARY}") add_library(apps OBJECT apps.cpp apps.h - SoilMoisture.cpp SoilMoisture.h) -target_include_directories(apps PUBLIC ${INCLUDE_DIRECTORIES}) + SoilMoisture.cpp SoilMoisture.h + diller_util.cpp diller_util.h) +target_include_directories(apps PUBLIC + "${PROJECT_SOURCE_DIR}/include" + "${PROJECT_SOURCE_DIR}/json/src" + ) -foreach(app ${APPS}) - add_executable(${app} ${app}.cpp $ ${${app}_SOURCES}) +function(add_app) + cmake_parse_arguments(ADD_APP "" "NAME" "EXTRA" ${ARGN}) - target_include_directories(${app} PUBLIC ${INCLUDE_DIRECTORIES}) + set(app "${ADD_APP_NAME}") - target_link_libraries(${app} - ble - trygvis-sensor - ${LIBRARIES} - ${Boost_LIBRARIES} - ${BLUEZ_LIBRARIES} - ${PQXX_LIBRARIES} - ${CMAKE_THREAD_LIBS_INIT}) + add_executable(${app} ${app}.cpp $ "${ADD_APP_EXTRA}") + + target_include_directories(${app} PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR} + "${PROJECT_SOURCE_DIR}/include" + "${PROJECT_SOURCE_DIR}/json/src" + "${PROJECT_SOURCE_DIR}/gsl/include" + "${PROJECT_SOURCE_DIR}/sensor/include" + ) + target_link_libraries(${app} + ble + trygvis-sensor + ${LIBRARIES} + ${Boost_LIBRARIES} + ${BLUEZ_LIBRARIES} + ${PQXX_LIBRARIES} + ${CMAKE_THREAD_LIBS_INIT} + ) install(TARGETS ${app} RUNTIME DESTINATION bin) -endforeach() +endfunction() + +add_app(NAME ble-inspect-device) +add_app(NAME ble-scan) +add_app(NAME sample-add-timestamp) +add_app(NAME sample-convert) +add_app(NAME sample-select) +add_app(NAME sm-db-insert) +add_app(NAME sm-db-select) +add_app(NAME sm-get-value) +add_app(NAME sm-serial-read) +add_app(NAME sm-serial-read-all) + +if(MOSQUITTO_OK STREQUAL "OK") + set(MQTT_SOURCES mqtt_support.cpp mqtt_support.h) + add_app(NAME mqtt-publish EXTRA "${MQTT_SOURCES}") + add_app(NAME sm-diller EXTRA "${MQTT_SOURCES}") +endif() diff --git a/apps/SoilMoisture.cpp b/apps/SoilMoisture.cpp index 13578f6..4b9dfe5 100644 --- a/apps/SoilMoisture.cpp +++ b/apps/SoilMoisture.cpp @@ -16,6 +16,7 @@ auto bluetooth_base_uuid = const boost::uuids::uuid soil_moisture_service = makeUuid(trygvis_io_base_uuid, 0x00, 0x10); const boost::uuids::uuid soil_moisture_characteristic = makeUuid(trygvis_io_base_uuid, 0x00, 0x11); const boost::uuids::uuid temperature_characteristic = makeUuid(bluetooth_base_uuid, 0x2a, 0x1e); +const boost::uuids::uuid light_characteristic = makeUuid(trygvis_io_base_uuid, 0x00, 0x12); using namespace trygvis::bluetooth; @@ -64,13 +65,15 @@ ByteBuffer createSetUpdateInterval(uint8_t sensor, uint8_t interval_in_seconds) SoilMoisture SoilMoisture::create(shared_ptr gatt) { gatt->discoverServices(); - auto service = gatt->findService(soil_moisture_service); + o> s = gatt->findService(soil_moisture_service); - if (!service) { + if (!s) { throw runtime_error("The device is missing the soil moisture service"); } - auto c = service->findCharacteristic(soil_moisture_characteristic); + shared_ptr &service = *s; + + o> c = service->findCharacteristic(soil_moisture_characteristic); if (!c) { throw runtime_error("The device is missing the soil moisture characteristic"); @@ -78,16 +81,20 @@ SoilMoisture SoilMoisture::create(shared_ptr gatt) { auto temperature = service->findCharacteristic(temperature_characteristic); - return SoilMoisture(gatt, *service, c.get(), temperature); + auto light = service->findCharacteristic(light_characteristic); + + return SoilMoisture(gatt, service, c.operator*(), temperature, light); } -SoilMoisture::SoilMoisture(const shared_ptr &gatt, BluetoothGattService &s, - const BluetoothGattCharacteristic &soilMoistureCharacteristic, - const o temperatureCharacteristic) +SoilMoisture::SoilMoisture(const shared_ptr &gatt, + const shared_ptr &s, + const BluetoothGattCharacteristicPtr &soilMoistureCharacteristic, + const o temperatureCharacteristic, + const o lightCharacteristic) : gatt(gatt), s(s), soilMoistureCharacteristic(soilMoistureCharacteristic), - temperatureCharacteristic(temperatureCharacteristic) {} + temperatureCharacteristic(temperatureCharacteristic), lightCharacteristic(lightCharacteristic) { } -ByteBuffer SoilMoisture::writeAndRead(const BluetoothGattCharacteristic &c, ByteBuffer &requestBytes) { +ByteBuffer SoilMoisture::writeAndRead(const BluetoothGattCharacteristicPtr &c, ByteBuffer &requestBytes) { requestBytes.setCursor(0); uint8_t expectedCode = requestBytes.get8(0); @@ -131,14 +138,15 @@ string SoilMoisture::getName(uint8_t sensor) { buffer.copy(bytes, bytesLeft); if (bytesLeft == 0 || bytesLeft < (bytes[0] + 1)) { - throw runtime_error("Bad response from device. buffer size: " + to_string(bytesLeft) + ", buffer[0]=" + to_string((int)bytes[0])); + throw runtime_error("Bad response from device. buffer size: " + to_string(bytesLeft) + ", buffer[0]=" + + to_string((int) bytes[0])); } - return string((const char *)&bytes[1], bytes[0]); + return string((const char *) &bytes[1], bytes[0]); } bool SoilMoisture::hasTemperatureSensor() { - return temperatureCharacteristic.is_initialized(); + return (bool) temperatureCharacteristic; } /** @@ -149,9 +157,7 @@ o SoilMoisture::readTemperature() { return o(); } - auto &c = temperatureCharacteristic.get(); - - auto responseBytes = gatt->readValue(c); + auto responseBytes = gatt->readValue(*temperatureCharacteristic); if (responseBytes.getSize() < 2) { throw runtime_error("Unexpected number of bytes read: " + to_string(responseBytes.getSize())); @@ -168,5 +174,19 @@ o SoilMoisture::readTemperature() { return o(temperature); } +void SoilMoisture::setLight(uint8_t light, uint8_t value) { + if (!lightCharacteristic) { + return; + } + + auto responseBytes = gatt->readValue(*lightCharacteristic); + + if (responseBytes.getSize() < 2) { + throw runtime_error("Unexpected number of bytes read: " + to_string(responseBytes.getSize())); + } + + bitset<8> b0 = responseBytes.read8(); +} + } } diff --git a/apps/SoilMoisture.h b/apps/SoilMoisture.h index 99b698d..0ad1d15 100644 --- a/apps/SoilMoisture.h +++ b/apps/SoilMoisture.h @@ -2,13 +2,14 @@ #include #include +#include namespace trygvis { namespace sensor { using namespace trygvis::bluetooth; -template -using o = boost::optional; +template +using o = std::experimental::optional; enum class sm_cmd_code : uint8_t { SM_CMD_GET_SENSOR_COUNT = 1, @@ -18,6 +19,8 @@ enum class sm_cmd_code : uint8_t { SM_CMD_SET_SENSOR_NAME = 5, SM_CMD_GET_SENSOR_NAME = 6, SM_CMD_SET_UPDATE_INTERVAL = 7, + // 8 is used + SM_CMD_SET_LIGHT = 9, SM_CMD_FAIL = 255, }; @@ -38,17 +41,22 @@ public: o readTemperature(); + void setLight(uint8_t light, uint8_t value); + private: - SoilMoisture(const shared_ptr &gatt, BluetoothGattService &s, - const BluetoothGattCharacteristic &soilMoistureCharacteristic, - const o temperatureCharacteristic); + SoilMoisture(const shared_ptr &gatt, + const shared_ptr &s, + const shared_ptr &soilMoistureCharacteristic, + const o temperatureCharacteristic, + const o lightCharacteristic); - ByteBuffer writeAndRead(const BluetoothGattCharacteristic &c, ByteBuffer &requestBytes); + ByteBuffer writeAndRead(const BluetoothGattCharacteristicPtr &c, ByteBuffer &requestBytes); shared_ptr gatt; - BluetoothGattService &s; - const BluetoothGattCharacteristic &soilMoistureCharacteristic; - const o temperatureCharacteristic; + shared_ptr s; + const BluetoothGattCharacteristicPtr soilMoistureCharacteristic; + const o temperatureCharacteristic; + const o lightCharacteristic; }; } } diff --git a/apps/apps.cpp b/apps/apps.cpp index b21d27e..56abc7b 100644 --- a/apps/apps.cpp +++ b/apps/apps.cpp @@ -56,7 +56,7 @@ const po::options_description logging_options() { void setup_logging(string app_name) { Appender *console = new ConsoleAppender(true, true); - string pattern = string("%-5p ") /*"%6r "*/ + app_name + "/%-20c %m%n"; // add %M (function name) + string pattern = "%-5p %D{%Y-%m-%d %H:%M:%S} " + app_name + "/%-20c %m%n"; // add %M (function name) PatternLayout *layout = new PatternLayout(LOG4CPLUS_TEXT(pattern)); console->setLayout(auto_ptr(layout)); diff --git a/apps/apps.h b/apps/apps.h index f3d6eae..9173ae0 100644 --- a/apps/apps.h +++ b/apps/apps.h @@ -8,6 +8,8 @@ #include #include #include +#include +#include namespace trygvis { namespace apps { @@ -22,12 +24,61 @@ 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) {} + missing_key(const json::string_t key) : runtime_error("Missing key: " + key), key(key) { } const json::string_t key; }; -template +class waitable { +protected: + std::condition_variable cv; + std::mutex mutex; + + waitable() { + } + + virtual ~waitable() { + } + +public: + void wait() { + std::unique_lock lock(mutex); + cv.wait(lock); + } + + template + void wait(Predicate predicate) { + std::unique_lock lock(mutex); + cv.wait(lock, predicate); + } + + template + std::cv_status wait_for(const std::chrono::duration &rel_time) { + std::unique_lock lock(mutex); + return cv.wait_for(lock, rel_time); + } + + template + bool wait_for(const std::chrono::duration &rel_time, Predicate predicate) { + std::unique_lock lock(mutex); + return cv.wait_for(lock, rel_time, predicate); + } + + template + std::cv_status wait_until(const std::chrono::time_point &timeout_time) { + std::unique_lock lock(mutex); + return cv.wait_until(lock, timeout_time); + }; + + template + bool wait_until(const std::chrono::time_point &timeout_time, + Predicate predicate) { + std::unique_lock lock(mutex); + return cv.wait_until(lock, timeout_time, predicate); + } +}; + +template T get(json j, std::string key) { auto ref = j[key]; @@ -41,7 +92,7 @@ T get(json j, std::string key) { class app_execution { public: app_execution(po::options_description desc, po::variables_map vm, Logger logger) - : desc(desc), vm(vm), logger(logger) {} + : desc(desc), vm(vm), logger(logger) { } const po::options_description desc; const po::variables_map vm; @@ -52,7 +103,7 @@ public: class app { public: - app(std::string app_name) : app_name(app_name) {} + app(std::string app_name) : app_name(app_name) { } app(const app &) = delete; @@ -62,9 +113,9 @@ public: app &operator=(const app &) = delete; - virtual void add_options(po::options_description_easy_init &options) {} + virtual void add_options(po::options_description_easy_init &options) { } - virtual void add_extra_options(po::options_description &options) {} + virtual void add_extra_options(po::options_description &options) { } virtual int main(app_execution &execution) = 0; @@ -75,12 +126,12 @@ std::string get_hostname(); int real_main(app *app, int argc, const char *argv[]); -template +template class noop_delete { public: - void operator()(T *) const {} + void operator()(T *) const { } }; -static inline void noop_deleter(void *) {} +static inline void noop_deleter(void *) { } } } diff --git a/apps/ble-inspect-device.cpp b/apps/ble-inspect-device.cpp index 2140e03..baee93a 100644 --- a/apps/ble-inspect-device.cpp +++ b/apps/ble-inspect-device.cpp @@ -29,11 +29,11 @@ public: gatt->discoverServices(); - vector services = gatt->getServices(); + auto services = gatt->getServices(); cout << "Device has " << services.size() << " services" << endl; for (auto &s : services) { - const vector characteristics = s->getCharacteristics(); + auto characteristics = s->getCharacteristics(); cout << "Service: UUID: " << s->getUuid() << ", has " << characteristics.size() << " characteristics" << endl; @@ -60,10 +60,10 @@ public: scan_callback(device); return EXIT_SUCCESS; - } catch (std::runtime_error ex) { + } catch (std::runtime_error &ex) { cout << "std::runtime_error: " << ex.what() << endl; return EXIT_FAILURE; - } catch (std::exception ex) { + } catch (std::exception &ex) { cout << "std::exception: " << ex.what() << endl; return EXIT_FAILURE; } diff --git a/apps/ble-scan.cpp b/apps/ble-scan.cpp index 158af14..b148752 100644 --- a/apps/ble-scan.cpp +++ b/apps/ble-scan.cpp @@ -67,10 +67,10 @@ public: for_each(begin(seen_devices), end(seen_devices), [&](auto mac) { cout << mac.str() << endl; }); return EXIT_SUCCESS; - } catch (std::runtime_error ex) { + } catch (std::runtime_error &ex) { cout << "std::runtime_error: " << ex.what() << endl; return EXIT_FAILURE; - } catch (std::exception ex) { + } catch (std::exception &ex) { cout << "std::exception: " << ex.what() << endl; return EXIT_FAILURE; } diff --git a/apps/diller_util.cpp b/apps/diller_util.cpp new file mode 100644 index 0000000..420ebc6 --- /dev/null +++ b/apps/diller_util.cpp @@ -0,0 +1,19 @@ +#include "diller_util.h" + +#include + +namespace trygvis { +namespace diller { + +using namespace std; + +string diller_topic_for_property_value(const string &device, const string &property) { + return "/diller/" + device + "/property/" + property + "/value"; +} + +string diller_topic_for_property_name(const string &device, const string &property) { + return "/diller/" + device + "/property/" + property + "/name"; +} + +}; // namespace diller +}; // namespace trygvis diff --git a/apps/diller_util.h b/apps/diller_util.h new file mode 100644 index 0000000..c7b74f2 --- /dev/null +++ b/apps/diller_util.h @@ -0,0 +1,14 @@ +#pragma once + +#include + +namespace trygvis { +namespace diller { + +using std::string; + +string diller_topic_for_property_value(const string &device, const string &property); +string diller_topic_for_property_name(const string &device, const string &property); + +}; // namespace diller +}; // namespace trygvis diff --git a/apps/mqtt_support.h b/apps/mqtt_support.h index 48a6c39..ab319fd 100644 --- a/apps/mqtt_support.h +++ b/apps/mqtt_support.h @@ -2,10 +2,10 @@ #define TRYGVIS_MQTT_SUPPORT_H #include +#include #include #include #include -#include #include #include #include @@ -13,16 +13,14 @@ #include #include #include "mosquitto.h" +#include "apps.h" namespace trygvis { namespace mqtt_support { -template -using o = std::experimental::optional; - +using namespace trygvis::apps; using namespace std; using namespace log4cplus; -using namespace gsl; static inline string error_to_string(int rc) { @@ -32,6 +30,26 @@ string error_to_string(int rc) { return string(mosquitto_strerror(rc)); } +static +vector mqtt_tokenize_topic(string path) { + char **topics; + int topic_count; + int i; + + mosquitto_sub_topic_tokenise(path.c_str(), &topics, &topic_count); + + vector res; + for (i = 0; i < topic_count; i++) { + if (topics[i] != NULL) { + res.emplace_back(topics[i]); + } + } + + mosquitto_sub_topic_tokens_free(&topics, topic_count); + + return res; +} + class mqtt_error : public std::runtime_error { public: @@ -79,7 +97,7 @@ enum mqtt_client_personality { }; template -class mqtt_client : private mqtt_lib { +class mqtt_client : public waitable, private mqtt_lib { template struct personality_tag { }; @@ -101,8 +119,8 @@ class mqtt_client : private mqtt_lib { // bool should_reconnect_; int unacked_messages_; - condition_variable cv; - mutex cv_mutex; +// condition_variable cv; +// mutex cv_mutex; void assert_success(const string &function, int rc) { if (rc != MOSQ_ERR_SUCCESS) { @@ -113,9 +131,20 @@ class mqtt_client : private mqtt_lib { public: mqtt_client(const string &host, const int port, const int keep_alive, const o &client_id, const bool clean_session) : - host(host), port(port), connecting_(false), connected_(false), /*should_reconnect_(false),*/ - keep_alive(keep_alive), unacked_messages_(0) { - mosquitto = mosquitto_new(client_id ? (*client_id).c_str() : nullptr, clean_session, this); + host(host), port(port), connecting_(false), connected_(false), /*should_reconnect_(false),*/ + keep_alive(keep_alive), unacked_messages_(0) { + const char *id = nullptr; + + if (client_id) { + id = client_id->c_str(); + } + else { + if (!clean_session) { + throw mqtt_error("If client id is not specified, clean session must be true", MOSQ_ERR_INVAL); + } + } + + mosquitto = mosquitto_new(id, clean_session, this); if (!mosquitto) { string err = strerror(errno); throw runtime_error("Could not initialize mosquitto instance: " + err); @@ -162,11 +191,6 @@ private: } public: - void wait() { - unique_lock lk(cv_mutex); - cv.wait(lk); - } - int unacked_messages() { guard lock(this_mutex); return unacked_messages_; @@ -235,7 +259,7 @@ private: void on_disconnect_wrapper(int rc) { guard lock(this_mutex); - LOG4CPLUS_INFO(logger, "Disconnected"); + LOG4CPLUS_INFO(logger, "Disconnected, rc=" << error_to_string(rc)); bool was_connecting = connecting_, was_connected = connected_; connecting_ = connected_ = false; @@ -320,6 +344,18 @@ public: assert_success("mosquitto_subscribe", rc); } + void publish(int *mid, const string &topic, int qos, bool retain, const string &s) { + auto len = s.length(); + + auto int_max = std::numeric_limits::max(); + + if (len > int_max) { + len = static_cast(int_max); + } + + publish(mid, topic, qos, retain, static_cast(len), s.c_str()); + } + void publish(int *mid, const string &topic, int qos, bool retain, int payload_len, const void *payload) { // if (!connected_) { // throw mqtt_error("not connected", MOSQ_ERR_NO_CONN); @@ -329,7 +365,7 @@ public: int rc = mosquitto_publish(mosquitto, mid, topic.c_str(), payload_len, payload, qos, retain); - if(rc == MOSQ_ERR_SUCCESS) { + if (rc == MOSQ_ERR_SUCCESS) { guard lock(this_mutex); unacked_messages_++; } diff --git a/apps/sm-diller.cpp b/apps/sm-diller.cpp new file mode 100644 index 0000000..2b1d63d --- /dev/null +++ b/apps/sm-diller.cpp @@ -0,0 +1,423 @@ +#include "ble/Bluetooth.h" +#include "SoilMoisture.h" +#include "diller_util.h" +#include "trygvis/sensor.h" +#include "trygvis/sensor/io.h" +#include "apps.h" +#include "mqtt_support.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace trygvis { +namespace apps { + +// I'm lazy +using namespace std; +using namespace std::chrono; +using namespace trygvis::apps; +using namespace trygvis::bluetooth; +using namespace trygvis::sensor; +using namespace trygvis::sensor::io; +using namespace trygvis::mqtt_support; +using namespace trygvis::diller; + +static struct { + string server; + string client_id; + int sleep_time; +} opts; + +struct device_command { + virtual ~device_command() { + } +}; + +struct set_light final : public virtual device_command { + set_light(uint8_t light, uint8_t value) : light(light), value(value) { + } + + virtual ~set_light() { + } + + const uint8_t light; + const uint8_t value; +}; + +struct diller_command { + diller_command() { + cerr << "diller_command" << endl; + } + + virtual ~diller_command() { + cerr << "~diller_command" << endl; + } +}; + +struct publish_soil_moisture_value final : public virtual diller_command { + publish_soil_moisture_value(uint8_t sensor, int value) : sensor(sensor), value(value) { + cerr << "publish_soil_moisture_value, this=" << (this) << endl; + } + + virtual ~publish_soil_moisture_value() { + cerr << "~publish_soil_moisture_value, this=" << (this) << endl; + } + + const int sensor; + const int value; +}; + +struct publish_soil_moisture_sensor_name final : public virtual diller_command { + publish_soil_moisture_sensor_name(uint8_t sensor, string name) : sensor(sensor), name(name) { + } + + virtual ~publish_soil_moisture_sensor_name() { + } + + const int sensor; + const string name; +}; + +atomic loop{true}; + +mutex main_mutex; +condition_variable main_cv; +std::list> to_device; +std::list> to_diller; + +class diller_mqtt_client : public mqtt_client { +public: + diller_mqtt_client(const Mac mac, const string &host, const int port, const int keep_alive, + const o &client_id, const bool clean_session) : + mqtt_client(host, port, keep_alive, client_id, clean_session), mac(mac), + light_path(diller_topic_for_property_value(mac.str(), "light")) { + } + + void run() { + connect(); + + while (loop) { + poll(); + + std::unique_lock lock(main_mutex); + auto res = main_cv.wait_for(lock, 1s); + + if (res == cv_status::no_timeout) { + if (!to_diller.empty()) { + diller_command &command = *to_diller.front(); + + if (typeid(command) == typeid(struct publish_soil_moisture_value &)) { + auto cmd = dynamic_cast(command); + + LOG4CPLUS_DEBUG(logger, "Publishing soil moisture value: sensor=" << cmd.sensor + << ", value=" << cmd.value); + + int mid; + + const string topic = diller_topic_for_property_value(mac.str(), "soil-moisture-" + + std::to_string(cmd.sensor)); + auto qos = 0; + publish(&mid, topic, qos, false, std::to_string(cmd.value)); + + LOG4CPLUS_DEBUG(logger, "Published as " << mid); + } else if (typeid(command) == typeid(struct publish_soil_moisture_sensor_name &)) { + auto cmd = dynamic_cast(command); + + LOG4CPLUS_DEBUG(logger, "Publishing soil moisture sensor name: sensor=" << cmd.sensor + << ", name=" << cmd.name); + + int mid; + + const string topic = diller_topic_for_property_name(mac.str(), "soil-moisture-" + + std::to_string(cmd.sensor)); + auto qos = 0; + publish(&mid, topic, qos, true, cmd.name); + + LOG4CPLUS_DEBUG(logger, "Published as " << mid); + } + + to_diller.pop_front(); + } + } + } + + LOG4CPLUS_INFO(logger, "Stopping"); + + disconnect(); + + LOG4CPLUS_INFO(logger, "Stopped"); + } + +protected: + void on_connect(int rc) override { + if (rc == MOSQ_ERR_SUCCESS) { + LOG4CPLUS_INFO(logger, "Subscribing to " << light_path); + subscribe(nullptr, light_path, 0); + } + } + + void on_message(const struct mosquitto_message *message) override { + LOG4CPLUS_INFO(logger, "got message: " << message->topic); + + auto segments = mqtt_tokenize_topic(message->topic); + + // /diller/aa:bb:cc:dd:ee:ff/property/light/value + if (segments.size() != 5 || + segments[0] != "diller" || + // $mac + segments[2] != "property" || + // $property + segments[4] != "value") { + LOG4CPLUS_INFO(logger, "Unknown message topic: " << message->topic); + return; + } + + if (message->payloadlen != 1) { + LOG4CPLUS_WARN(logger, "Unknown message payload, expected exactly one byte, got " << message->payloadlen << + " bytes on topic " << message->topic); + return; + } + + auto property_name = segments[3]; + + if (property_name == "light") { + std::unique_lock lock(main_mutex); + uint8_t value = static_cast(message->payload)[0]; + + const int light = 0; + + LOG4CPLUS_INFO(logger, + "Pushing command set_light(light=" << light << ", value=" << static_cast(value) << ")"); + + to_device.push_back(make_unique(light, value)); + main_cv.notify_all(); + } else { + LOG4CPLUS_INFO(logger, "Unknown property name " << property_name); + } + } + +private: + const Mac mac; + const string light_path; + + Logger logger = Logger::getInstance(LOG4CPLUS_TEXT("diller_mqtt_client")); +}; + +void mqtt_thread(Mac &mac) { + Logger logger = Logger::getInstance(LOG4CPLUS_TEXT("mqtt_thread")); + + try { + diller_mqtt_client mqtt_client(mac, opts.server, 1883, 60, o(opts.client_id), true); + + mqtt_client.run(); + } catch (std::runtime_error &ex) { + LOG4CPLUS_WARN(logger, "std::exception: " << ex.what()); + } catch (std::exception &ex) { + LOG4CPLUS_WARN(logger, "std::exception: " << ex.what()); + } +} + +class sm_diller : public app { +public: + sm_diller() : app("sm-diller") { } + + ~sm_diller() = default; + + std::chrono::duration sleep_time; + vector sensorIndexes; + vector> sensors; + + void add_options(po::options_description_easy_init &options) override { + auto opt_server = po::value(&opts.server)->required(); + auto opt_client_id = po::value(&opts.client_id)->default_value("sm-diller-" + get_hostname()); + auto default_sleep = po::value<>(&opts.sleep_time)->default_value(10); + + options("server", opt_server, "MQTT server"); + options("client-id", opt_client_id, "MQTT client id"); + + options("device", po::value()->required(), "MAC of device to poll"); + options("sensor", po::value<>(&sensorIndexes)->multitoken(), + "Sensor to poll, defaults to all"); + options("sleep", default_sleep, + "How long to sleep in seconds between each poll. If not given, it will exit after first poll"); + } + + int main(app_execution &execution) override { + BluetoothSystem bluetoothSystem; + + auto desc = execution.desc; + auto vm = execution.vm; + + std::thread mqtt_t; + int ret; + + try { + if (!vm.count("device")) { + cerr << "Missing required option: device" << endl; + cerr << desc << "\n"; + return EXIT_FAILURE; + } + + auto mac_string = vm["device"].as(); + + Mac mac = Mac::parseMac(mac_string); + + auto adapter = bluetoothSystem.getAdapter("0"); + + auto device = adapter->getDevice(mac); + + sleep_time = std::chrono::seconds(opts.sleep_time); + + mqtt_t = std::thread(mqtt_thread, std::ref(mac)); + + do { + try { + LOG4CPLUS_INFO(execution.logger, "Connecting to device: " << device->getMac().str()); + auto gatt = device->connectGatt(); + + withConnection(execution.logger, gatt); + } catch (BluetoothException &e) { + LOG4CPLUS_ERROR(execution.logger, "Bluetooth error: " << e.what()); + } catch (runtime_error &e) { + LOG4CPLUS_ERROR(execution.logger, "Exception: " << e.what()); + } + + LOG4CPLUS_DEBUG(execution.logger, "Sleeping for " << std::to_string(sleep_time.count())); + + this_thread::sleep_until(system_clock::now() + sleep_time); + } while (loop); + + unique_lock lock(main_mutex); + main_cv.notify_all(); + + ret = EXIT_SUCCESS; + } catch (std::runtime_error &ex) { + LOG4CPLUS_WARN(execution.logger, "std::runtime_error: " << ex.what()); + ret = EXIT_FAILURE; + } catch (std::exception &ex) { + LOG4CPLUS_WARN(execution.logger, "std::exception: " << ex.what()); + ret = EXIT_FAILURE; + } + + if (mqtt_t.joinable()) { + mqtt_t.join(); + } + + return ret; + } + + void read_sensors(SoilMoisture &soilMoisture, string mac) { + auto epoch = system_clock::now().time_since_epoch(); + auto timestamp = duration_cast(epoch).count(); + + auto tempO = soilMoisture.readTemperature(); + if (tempO) { + tempO.value(); + } + + for (auto s : sensors) { + auto sensor = s.first; + auto name = s.second; + + uint16_t value = soilMoisture.getValue(sensor); + + { + unique_lock lock(main_mutex); + to_diller.push_back(make_unique(sensor, value)); + } + } + + main_cv.notify_all(); + } + + void withConnection(const Logger &logger, shared_ptr gatt) { + SoilMoisture soilMoisture = SoilMoisture::create(gatt); + + if (sensors.empty()) { + 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 (uint8_t i = 0; i < sensorCount; i++) { + sensorIndexes.push_back(i); + } + } + + for_each(begin(sensorIndexes), end(sensorIndexes), [&](uint8_t i) { + if (i >= sensorCount) { + // Ignore invalid sensors + return; + } + + auto name = soilMoisture.getName(i); + sensors.push_back(make_pair(i, name)); + + unique_lock lock(main_mutex); + to_diller.push_back(make_unique(i, name)); + }); + + main_cv.notify_all(); + } + + auto mac = gatt->getDevice().getMac().str(); + if (!loop) { + read_sensors(soilMoisture, mac); + } else { + std::chrono::time_point target_time; + target_time = system_clock::now(); + + do { + target_time = target_time + seconds(sleep_time); + + read_sensors(soilMoisture, mac); + + do { + unique_lock lock(main_mutex); + auto res = main_cv.wait_until(lock, target_time); + + if (res == cv_status::no_timeout) { + LOG4CPLUS_DEBUG(logger, "no_timeout, to_device.size()=" << to_device.size()); + } + + if (!to_device.empty()) { + device_command &command = *std::move(to_device.front()); + to_device.pop_front(); + if (typeid(command) == typeid(set_light &)) { + auto sl = dynamic_cast(command); + LOG4CPLUS_DEBUG(logger, "performing Set light: light=" << static_cast(sl.light) + << ", value=" << static_cast(sl.value)); + + try { + soilMoisture.setLight(sl.light, sl.value); + } catch (std::runtime_error &e) { + LOG4CPLUS_WARN(logger, + "Could not execute setLight, light=" << static_cast(sl.light) + << ", value=" << static_cast(sl.value) + << ", error: " << e.what()); + } + } + } + } while (target_time > system_clock::now()); + } while (loop); + } + } +}; + +} +} + +int main(int argc, const char *argv[]) { + using app_t = trygvis::apps::sm_diller; + + return real_main(new app_t(), argc, argv); +} diff --git a/apps/sm-get-value.cpp b/apps/sm-get-value.cpp index 0572155..42f5da3 100644 --- a/apps/sm-get-value.cpp +++ b/apps/sm-get-value.cpp @@ -35,9 +35,9 @@ public: vector> sensors; KeyDictionary dict; - SampleKey *hostname_key = dict.indexOf("hostname"); - SampleKey *device_key = dict.indexOf("device"); - SampleKey *timestamp_key = dict.indexOf("timestamp_ms"); + const SampleKey *hostname_key = dict.indexOf("hostname"); + const SampleKey *device_key = dict.indexOf("device"); + const SampleKey *timestamp_key = dict.indexOf("timestamp_ms"); void add_options(po::options_description_easy_init &options) override { auto default_sleep = po::value<>(&sleepTime)->default_value(0); @@ -96,10 +96,10 @@ public: } while (loop); return EXIT_SUCCESS; - } catch (std::runtime_error ex) { + } catch (std::runtime_error &ex) { cout << "std::runtime_error: " << ex.what() << endl; return EXIT_FAILURE; - } catch (std::exception ex) { + } catch (std::exception &ex) { cout << "std::exception: " << ex.what() << endl; return EXIT_FAILURE; } @@ -118,7 +118,7 @@ public: auto tempO = soilMoisture.readTemperature(); if (tempO) { - sample.set(dict.indexOf("temperature"), std::to_string(tempO.get())); + sample.set(dict.indexOf("temperature"), std::to_string(tempO.value())); } int i = 0; diff --git a/ble/BluetoothImpl.h b/ble/BluetoothImpl.h index 4627443..8663b20 100644 --- a/ble/BluetoothImpl.h +++ b/ble/BluetoothImpl.h @@ -26,8 +26,6 @@ namespace bluetooth { // Utility typedefs typedef boost::uuids::uuid uuid_t; -template -using o = boost::optional; using namespace log4cplus; // Logging @@ -43,18 +41,45 @@ protected: // Shared classes +namespace detail { + +template +struct constify2; + +template +struct constify2 { + typedef T *type; +}; + +template +struct constify2 { + typedef T const *type; +}; + +} + +template +class CollectionImpl : public Collection { +public: + CollectionImpl(B &b) : b(b) { + } + +private: + B &b; +}; + class DefaultBluetoothGattCharacteristic : protected LogSetup, public BluetoothGattCharacteristic { public: - DefaultBluetoothGattCharacteristic(BluetoothGattService &service, uint16_t handle, uuid_t uuid, uint8_t properties, - uint16_t valueHandle) - : LogSetup("BluetoothGattCharacteristic"), service(service), handle(handle), uuid(uuid), - properties(properties), valueHandle(valueHandle) { + DefaultBluetoothGattCharacteristic(const BluetoothGattServicePtr &service, uint16_t handle, uuid_t uuid, + uint8_t properties, uint16_t valueHandle) + : LogSetup("BluetoothGattCharacteristic"), service(service), handle(handle), uuid(uuid), + properties(properties), valueHandle(valueHandle) { } virtual ~DefaultBluetoothGattCharacteristic() { } - BluetoothGattService &getService() const { + BluetoothGattServicePtr getService() const { return service; } @@ -75,22 +100,29 @@ public: } protected: - BluetoothGattService &service; + BluetoothGattServicePtr service; uint16_t handle; uuid_t uuid; uint8_t properties; uint16_t valueHandle; }; +template class DefaultBluetoothGattService : public BluetoothGattService { public: - DefaultBluetoothGattService(BluetoothDevice &device, const uuid_t uuid, const uint16_t handle, + DefaultBluetoothGattService(DeviceType &device, const uuid_t uuid, const uint16_t handle, const uint16_t endGroupHandle) - : device(device), uuid(uuid), handle(handle), endGroupHandle(endGroupHandle) { + : device(device), uuid(uuid), handle(handle), endGroupHandle(endGroupHandle) { + } + + DefaultBluetoothGattService(const DefaultBluetoothGattService &o) = delete; + + DefaultBluetoothGattService(DefaultBluetoothGattService &&o) noexcept + : device(std::move(o.device)), uuid(std::move(o.uuid)), handle(std::move(o.handle)), + endGroupHandle(std::move(o.endGroupHandle)), characteristics(std::move(o.characteristics)) { } virtual ~DefaultBluetoothGattService() { - removeCharacteristics(); } virtual BluetoothDevice &getDevice() const { @@ -109,62 +141,63 @@ public: return endGroupHandle; } - virtual const vector getCharacteristics() const { - return characteristics; + virtual vector> getCharacteristics() const { + vector> cs(characteristics.size()); + std::copy(begin(characteristics), end(characteristics), begin(cs)); + return cs; } - virtual void addCharacteristic(BluetoothGattCharacteristic *characteristic) { - characteristics.push_back(characteristic); + virtual void addCharacteristic(shared_ptr &&characteristic) { + characteristics.emplace_back(characteristic); } - virtual const o findCharacteristic(uuid_t uuid) const { - for (auto c: characteristics) { + virtual o> findCharacteristic(uuid_t uuid) { + for (auto &c: characteristics) { if (memcmp(c->getUuid().data, uuid.data, 16) == 0) { - return o(*c); + return o>(c); } } - return o(); + return o>(); } protected: - BluetoothDevice &device; + DeviceType &device; const uuid_t uuid; const uint16_t handle; const uint16_t endGroupHandle; - vector characteristics; - - void removeCharacteristics() { - for (auto &c: characteristics) { - delete c; - } - characteristics.clear(); - } + vector> characteristics; }; -template +template class DefaultBluetoothGatt : protected LogSetup, public BluetoothGatt { public: virtual _D &getDevice() const { return device; } - virtual const vector getServices() const { - return services; - }; +// virtual Collection getServices() const { +// return CollectionImpl>(services); +// } - virtual const o findService(uuid_t uuid) const { - for (auto s: services) { + virtual vector> getServices() const { + vector> ss(services.size()); + std::copy(begin(services), end(services), begin(ss)); + return ss; + } + + virtual o findService(uuid_t uuid) { + for (auto &s: services) { if (memcmp(s->getUuid().data, uuid.data, 16) == 0) { - return o(*s); + return o>(s); } } - return o(); + return o>(); } - virtual void addService(BluetoothGattService *service) { - services.push_back(service); + void addService(shared_ptr<_S> service) { + services.emplace_back(service); } protected: @@ -172,18 +205,18 @@ protected: } virtual ~DefaultBluetoothGatt() { - removeServices(); +// removeServices(); } void removeServices() { - for (auto s: services) { - delete s; - } +// for (auto s: services) { +// delete s; +// } services.clear(); } _D &device; - vector services; + vector> services; }; template @@ -199,8 +232,12 @@ public: } protected: - DefaultBluetoothDevice(A &adapter, Mac &mac) : LogSetup("BluetoothDevice"), - adapter(adapter), mac(mac) { + DefaultBluetoothDevice(A &adapter, Mac &mac) : + LogSetup("BluetoothDevice"), adapter(adapter), mac(mac) { + } + + DefaultBluetoothDevice(DefaultBluetoothDevice &&o) noexcept : + LogSetup("BluetoothDevice"), adapter(std::move(o.adapter)), mac(std::move(o.mac)) { } virtual ~DefaultBluetoothDevice() { @@ -223,7 +260,7 @@ class DefaultBluetoothAdapter : protected LogSetup, public BluetoothAdapter { public: protected: DefaultBluetoothAdapter(Mac &mac) : - LogSetup("BluetoothAdapter"), mac(mac) { + LogSetup("BluetoothAdapter"), mac(mac) { } Mac const &getMac() override { @@ -233,7 +270,7 @@ protected: Mac &mac; }; -shared_ptr getAdapterImpl(string name); +shared_ptr getAdapterImpl(string name); } }; diff --git a/ble/LinuxBluetooth.cpp b/ble/LinuxBluetooth.cpp index 587016c..d4073d1 100644 --- a/ble/LinuxBluetooth.cpp +++ b/ble/LinuxBluetooth.cpp @@ -28,6 +28,15 @@ class LinuxBluetoothAdapter; class LinuxBluetoothManager; +class LinuxBluetoothGattService : public DefaultBluetoothGattService { +public: + LinuxBluetoothGattService(LinuxBluetoothDevice &device, const uuid_t uuid, const uint16_t handle, + const uint16_t endGroupHandle) : DefaultBluetoothGattService(device, uuid, handle, endGroupHandle) { + }; + + LinuxBluetoothGattService(const LinuxBluetoothGattService &o) = delete; +}; + class LinuxBluetoothAdapter final : public DefaultBluetoothAdapter { public: LinuxBluetoothAdapter(int hciDeviceId, Mac &mac); @@ -72,7 +81,7 @@ private: LinuxBluetoothGatt *gatt; }; -class LinuxBluetoothGatt final : public DefaultBluetoothGatt { +class LinuxBluetoothGatt final : public DefaultBluetoothGatt { public: LinuxBluetoothGatt(LinuxBluetoothDevice &device); @@ -86,9 +95,9 @@ public: void discoverServices() override; - void writeValue(const BluetoothGattCharacteristic &c, const ByteBuffer &bytes) override; + void writeValue(const BluetoothGattCharacteristicPtr &c, const ByteBuffer &bytes) override; - ByteBuffer readValue(const BluetoothGattCharacteristic &c) override; + ByteBuffer readValue(const BluetoothGattCharacteristicPtr &c) override; private: void connect(); @@ -252,24 +261,24 @@ uuid_t readUuid(BluetoothDevice *device, const ByteBuffer &bytes) { return u; } -void LinuxBluetoothGatt::writeValue(const BluetoothGattCharacteristic &c, const ByteBuffer &bytes) { - LOG_DEBUG("Writing to characteristic " << c.getUuid() << ": " << bytes.toString()); +void LinuxBluetoothGatt::writeValue(const BluetoothGattCharacteristicPtr &c, const ByteBuffer &bytes) { + LOG_DEBUG("Writing to characteristic " << c->getUuid() << ": " << bytes.toString()); shared_ptr buffer(new uint8_t[MAX_MTU]); ByteBuffer out = ByteBuffer(buffer, MAX_MTU); - AttPdu::makeWrite(out, c.getValueHandle(), bytes); + AttPdu::makeWrite(out, c->getValueHandle(), bytes); ByteBuffer in = writeAndRead(out, buffer, MAX_MTU); AttPdu::parseWrite(in); } -ByteBuffer LinuxBluetoothGatt::readValue(const BluetoothGattCharacteristic &c) { +ByteBuffer LinuxBluetoothGatt::readValue(const BluetoothGattCharacteristicPtr &c) { shared_ptr buffer(new uint8_t[MAX_MTU]); ByteBuffer out = ByteBuffer(buffer, MAX_MTU); - AttPdu::makeRead(out, c.getValueHandle()); + AttPdu::makeRead(out, c->getValueHandle()); ByteBuffer in = writeAndRead(out, buffer, MAX_MTU); @@ -279,7 +288,7 @@ ByteBuffer LinuxBluetoothGatt::readValue(const BluetoothGattCharacteristic &c) { auto response = in.view(); - LOG_DEBUG("Value of characteristic " << c.getUuid() << "=" << response.toString()); + LOG_DEBUG("Value of characteristic " << c->getUuid() << "=" << response.toString()); return response; } @@ -308,7 +317,7 @@ void LinuxBluetoothGatt::discoverServices() { uuid_t u = readUuid(&device, data.value); - addService(new DefaultBluetoothGattService(device, u, data.handle, endGroupHandle)); + addService(make_shared(device, u, data.handle, endGroupHandle)); } // auto last = values.back(); @@ -358,7 +367,7 @@ void LinuxBluetoothGatt::discoverServices() { // ", valueHandle: 0x" << setw(4) << setfill('0') << hex << (int) valueHandle << // ", uuid: " << uuid; - s->addCharacteristic(new DefaultBluetoothGattCharacteristic(*s, c.handle, uuid, properties, valueHandle)); + s->addCharacteristic(std::move(make_shared(s, c.handle, uuid, properties, valueHandle))); } startHandle = lastHandle + (uint8_t) 2; diff --git a/include/ble/Bluetooth.h b/include/ble/Bluetooth.h index 8da602f..f2b5b13 100644 --- a/include/ble/Bluetooth.h +++ b/include/ble/Bluetooth.h @@ -2,7 +2,7 @@ #define BLUETOOTH_H #include -#include +#include #include #include #include @@ -15,6 +15,8 @@ namespace trygvis { namespace bluetooth { using namespace std; +template +using o = std::experimental::optional; struct SpecUuid { public: @@ -38,18 +40,21 @@ class BluetoothGattService; class BluetoothGattCharacteristic; +typedef shared_ptr BluetoothGattCharacteristicPtr; +typedef shared_ptr BluetoothGattServicePtr; + class BluetoothException : public runtime_error { public: BluetoothException(const BluetoothAdapter *adapter, string const &what) : - runtime_error(what), adapter(adapter), device(nullptr) { + runtime_error(what), adapter(adapter), device(nullptr) { } BluetoothException(const BluetoothDevice *device, string const &what) : - runtime_error(what), adapter(nullptr), device(device) { + runtime_error(what), adapter(nullptr), device(device) { } BluetoothException(string const &what) : - runtime_error(what), adapter(nullptr), device(nullptr) { + runtime_error(what), adapter(nullptr), device(nullptr) { } const BluetoothAdapter *adapter; @@ -83,12 +88,26 @@ private: uint8_t bytes[6]; }; +template +class Iterator { +public: +private: +}; + +template +class Collection { +public: + Iterator begin(); + + Iterator end(); +}; + class BluetoothGattCharacteristic { public: virtual ~BluetoothGattCharacteristic() { }; - virtual BluetoothGattService &getService() const = 0; + virtual BluetoothGattServicePtr getService() const = 0; virtual uint16_t getHandle() const = 0; @@ -112,11 +131,9 @@ public: virtual uint16_t getEndGroupHandle() const = 0; - virtual const vector getCharacteristics() const = 0; - - virtual void addCharacteristic(BluetoothGattCharacteristic *characteristic) = 0; + virtual vector> getCharacteristics() const = 0; - virtual const boost::optional findCharacteristic(boost::uuids::uuid uuid) const = 0; + virtual o findCharacteristic(boost::uuids::uuid uuid) = 0; }; class BluetoothGatt { @@ -126,23 +143,23 @@ public: virtual ~BluetoothGatt(); // No copying - BluetoothGatt(const BluetoothGatt&) = delete; + BluetoothGatt(const BluetoothGatt &) = delete; - BluetoothGatt& operator=(const BluetoothGatt&) = delete; + BluetoothGatt &operator=(const BluetoothGatt &) = delete; virtual BluetoothDevice &getDevice() const = 0; virtual bool isConnected() const = 0; - virtual void writeValue(const BluetoothGattCharacteristic &c, const ByteBuffer &bytes) = 0; + virtual void writeValue(const BluetoothGattCharacteristicPtr &c, const ByteBuffer &bytes) = 0; - virtual ByteBuffer readValue(const BluetoothGattCharacteristic &c) = 0; + virtual ByteBuffer readValue(const BluetoothGattCharacteristicPtr &c) = 0; virtual void discoverServices() = 0; - virtual const vector getServices() const = 0; + virtual vector> getServices() const = 0; - virtual const boost::optional findService(boost::uuids::uuid uuid) const = 0; + virtual o findService(boost::uuids::uuid uuid) = 0; }; class BluetoothDevice { @@ -168,7 +185,7 @@ public: virtual void runScan(std::function &device)>) = 0; - virtual shared_ptr getDevice(Mac &mac) = 0; + virtual shared_ptr getDevice(Mac &mac) = 0; protected: BluetoothAdapter(); diff --git a/json b/json index 2c720b2..c012b29 160000 --- a/json +++ b/json @@ -1 +1 @@ -Subproject commit 2c720b26abcd8ef757291f2baf1311a4aab58121 +Subproject commit c012b29ae5397af1608842ba915ad6b4133a0301 diff --git a/sensor/include/trygvis/sensor.h b/sensor/include/trygvis/sensor.h index c109a2a..09372b9 100644 --- a/sensor/include/trygvis/sensor.h +++ b/sensor/include/trygvis/sensor.h @@ -5,15 +5,16 @@ #include #include #include -#include +#include +#include namespace trygvis { namespace sensor { -using namespace std; +//using namespace std; -template -using o = boost::optional; +template +using o = std::experimental::optional; enum class sample_format_type { AUTO, @@ -24,7 +25,7 @@ enum class sample_format_type { RRD, }; -string to_string(const sample_format_type &arg); +std::string to_string(const sample_format_type &arg); std::ostream& operator<<(std::ostream& os, sample_format_type const& type); @@ -39,9 +40,9 @@ std::ostream& operator<<(std::ostream& os, time_resolution const& type); std::istream& operator>>(std::istream& is, time_resolution & type); -class sample_exception : public runtime_error { +class sample_exception : public std::runtime_error { public: - sample_exception(const string &what) : runtime_error(what) { + sample_exception(const std::string &what) : runtime_error(what) { } }; @@ -51,13 +52,13 @@ class SampleKey; class KeyDictionary; -using SampleKeyVector = vector; +using SampleKeyVector = std::vector; using SampleKeyIndex = SampleKeyVector::size_type; struct SampleKey { private: SampleKey(const SampleKey& that) = delete; - SampleKey(SampleKeyIndex index, const string &name) : index(index), name(name) { + SampleKey(SampleKeyIndex index, const std::string &name) : index(index), name(name) { if (name.length() == 0) { throw sample_exception("Bad sample key."); } @@ -72,7 +73,7 @@ public: } const SampleKeyIndex index; - const string name; + const std::string name; }; class KeyDictionary { @@ -88,7 +89,7 @@ public: KeyDictionary& operator=(const KeyDictionary&) = delete; - SampleKey *indexOf(const string &key) { + const SampleKey *indexOf(const std::string &key) { SampleKeyIndex i = 0; for (auto ptr = keys.cbegin(); ptr != keys.cend(); ptr++, i++) { if ((*ptr)->name == key) { @@ -111,8 +112,8 @@ public: return keys.at(i); } - vector findIndexes(SampleKeyVector &keys) { - vector indexes; + std::vector findIndexes(SampleKeyVector &keys) { + std::vector indexes; for (auto &key: keys) { auto index = indexOf(key->name); @@ -152,7 +153,7 @@ private: class SampleRecord { public: - typedef vector> vec; + typedef std::vector> vec; SampleRecord(KeyDictionary &dict) : dict(dict) { } @@ -179,19 +180,19 @@ public: return values.empty(); } - const o at(const SampleKey *key) const { + const o at(const SampleKey *key) const { SampleKeyIndex index = key->index; if (index >= values.size()) { - return o(); + return o(); } return values.at(index); } SampleRecord& set(const SampleKey *key, const std::string &value) { - values.resize(max(values.size(), key->index + 1)); + values.resize(std::max(values.size(), key->index + 1)); - values.at(key->index).reset(value); + values.at(key->index) = value; return *this; } @@ -199,9 +200,9 @@ public: template const o lexical_at(const SampleKey *key) const; - string to_string() const { + std::string to_string() const { SampleKeyIndex i = 0; - string s; + std::string s; for (auto ptr = values.begin(); ptr != values.end(); ptr++, i++) { auto o = *ptr; @@ -209,7 +210,7 @@ public: continue; } - auto value = o.get(); + auto value = *o; s += dict.at(i)->name + " = " + value + ", "; } diff --git a/sensor/include/trygvis/sensor/io.h b/sensor/include/trygvis/sensor/io.h index 71b0b84..304091b 100644 --- a/sensor/include/trygvis/sensor/io.h +++ b/sensor/include/trygvis/sensor/io.h @@ -187,7 +187,7 @@ public: void write(SampleRecord const &sample) override; private: - vector keys; + vector keys; shared_ptr stream; const SampleKey *timestamp_key; }; diff --git a/sensor/main/io.cpp b/sensor/main/io.cpp index 96a98f6..9e822c3 100644 --- a/sensor/main/io.cpp +++ b/sensor/main/io.cpp @@ -45,17 +45,17 @@ unique_ptr open_sample_output_stream( auto tsf = options.find_option(); - auto timestamp_key = dict.indexOf(tsf ? tsf.get()->name : "timestamp"); + auto timestamp_key = dict.indexOf(tsf ? tsf.value()->name : "timestamp"); return make_unique(output, dict, timestamp_key, of); } else if (type == sample_format_type::SQL) { auto tno = options.find_option(); - if (!tno.is_initialized()) { + if (!tno) { throw missing_required_option_error("table name"); } - return make_unique(move(output), dict, tno.get()->name); + return make_unique(move(output), dict, tno.value()->name); } else { throw sample_exception("No writer for format type: " + to_string(type)); } @@ -144,7 +144,7 @@ void CsvSampleOutputStream::write(SampleRecord const &sample) { auto o = sample.at(sampleKey); if (o) { - s << o.get(); + s << o.value(); } } @@ -152,7 +152,7 @@ void CsvSampleOutputStream::write(SampleRecord const &sample) { } void CsvSampleOutputStream::writeHeader() { - auto &s = *stream.get(); + auto &s = *stream; auto i = dict.begin(); while (i != dict.end()) { @@ -187,7 +187,7 @@ void JsonSampleOutputStream::write(SampleRecord const &sample) { auto value = sample.at(sampleKey); if (value) { - doc[key->name] = value.get(); + doc[key->name] = value.value(); } } } else { @@ -197,7 +197,7 @@ void JsonSampleOutputStream::write(SampleRecord const &sample) { if (o) { // Make sure that the key is registered in the dictionary dict.indexOf(sampleKey->name); - doc[sampleKey->name] = o.get(); + doc[sampleKey->name] = o.value(); } } } @@ -230,7 +230,7 @@ void KeyValueSampleOutputStream::write(SampleRecord const &sample) { } else { *s << ", "; } - *s << key->name << "=" << value.get(); + *s << key->name << "=" << value.value(); } } } else { @@ -245,7 +245,7 @@ void KeyValueSampleOutputStream::write(SampleRecord const &sample) { } // Make sure that the key is registered in the dictionary dict.indexOf(sampleKey->name); - *s << sampleKey->name << "=" << o.get(); + *s << sampleKey->name << "=" << o.value(); } } } @@ -260,7 +260,7 @@ RrdSampleOutputStream::RrdSampleOutputStream(shared_ptr stream, stream(move(stream)), timestamp_key(timestamp_key) { if (output_fields) { - for (auto field : output_fields.get()->fields) { + for (auto field : output_fields.value()->fields) { keys.emplace_back(dict.indexOf(field)); } } else { @@ -284,7 +284,7 @@ void RrdSampleOutputStream::write(SampleRecord const &sample) { return; } - auto timestamp = timestampO.get(); + auto timestamp = timestampO.value(); s << timestamp; @@ -303,7 +303,7 @@ void RrdSampleOutputStream::write(SampleRecord const &sample) { s << ":"; } - s << (value ? value.get() : "U"); + s << (value ? value.value() : "U"); } s << endl << flush; @@ -319,7 +319,7 @@ void SqlSampleOutputStream::write(SampleRecord const &sample) { fs.reserve(1024); vs.reserve(1024); - auto &s = *stream.get(); + auto &s = *stream; bool first = true; if (!dict.empty()) { @@ -336,7 +336,7 @@ void SqlSampleOutputStream::write(SampleRecord const &sample) { vs += ", "; } fs += "\"" + key->name + "\""; - vs += "'" + value.get() + "'"; + vs += "'" + value.value() + "'"; } } } else { @@ -354,7 +354,7 @@ void SqlSampleOutputStream::write(SampleRecord const &sample) { dict.indexOf(sample_key->name); fs += "\"" + sample_key->name + "\""; - vs += "'" + o.get() + "'"; + vs += "'" + o.value() + "'"; } } } diff --git a/sensor/main/sensor.cpp b/sensor/main/sensor.cpp index 1c0c666..0988e0d 100644 --- a/sensor/main/sensor.cpp +++ b/sensor/main/sensor.cpp @@ -80,7 +80,7 @@ const o SampleRecord::lexical_at(const SampleKey *key) const { return o(); } - return o(boost::lexical_cast(value.get())); + return o(boost::lexical_cast(*value)); } // //template diff --git a/sensor/test/SampleTest.cpp b/sensor/test/SampleTest.cpp index 8479e75..0fffc04 100644 --- a/sensor/test/SampleTest.cpp +++ b/sensor/test/SampleTest.cpp @@ -49,13 +49,13 @@ BOOST_AUTO_TEST_CASE(key_value_parser2) { SampleRecord& sample = buffer->samples[0]; auto it = dict.begin(); BOOST_CHECK_EQUAL((*it)->name, "now"); - BOOST_CHECK_EQUAL(!sample.at(*it).operator!(), true); - BOOST_CHECK_EQUAL(sample.at(*it).get(), "1"); + BOOST_CHECK_EQUAL(!!sample.at(*it), true); + BOOST_CHECK_EQUAL(sample.at(*it).value(), "1"); BOOST_CHECK_EQUAL((*it++)->index, 0); BOOST_CHECK_EQUAL((*it)->name, "sensor 1"); - BOOST_CHECK_EQUAL(!sample.at(*it).operator!(), true); - BOOST_CHECK_EQUAL(sample.at(*it).get(), "0.000999999833333"); + BOOST_CHECK_EQUAL(!!sample.at(*it), true); + BOOST_CHECK_EQUAL(sample.at(*it).value(), "0.000999999833333"); BOOST_CHECK_EQUAL((*it++)->index, 1); } @@ -112,12 +112,12 @@ BOOST_AUTO_TEST_CASE(key_value_parser_without_newline) { SampleRecord& sample = buffer->samples[0]; auto it = dict.begin(); BOOST_CHECK_EQUAL((*it)->name, "now"); - BOOST_CHECK_EQUAL(!sample.at(*it).operator!(), true); - BOOST_CHECK_EQUAL(sample.at(*it).get(), "1"); + BOOST_CHECK_EQUAL(!!sample.at(*it), true); + BOOST_CHECK_EQUAL(sample.at(*it).value(), "1"); BOOST_CHECK_EQUAL((*it++)->index, 0); BOOST_CHECK_EQUAL((*it)->name, "sensor"); - BOOST_CHECK_EQUAL(!sample.at(*it).operator!(), true); - BOOST_CHECK_EQUAL(sample.at(*it).get(), "123"); + BOOST_CHECK_EQUAL(!!sample.at(*it), true); + BOOST_CHECK_EQUAL(sample.at(*it).value(), "123"); BOOST_CHECK_EQUAL((*it++)->index, 1); } diff --git a/test/ByteBufferTest.cpp b/test/ByteBufferTest.cpp index 6a1c4cb..4a612d2 100644 --- a/test/ByteBufferTest.cpp +++ b/test/ByteBufferTest.cpp @@ -40,7 +40,7 @@ BOOST_AUTO_TEST_CASE(empty_buffer) { try { buffer.read8(); BOOST_FAIL("Expected exception"); - } catch (ByteBufferException e) { + } catch (ByteBufferException &e) { } } -- cgit v1.2.3