From b29e69270e012458f31438a3e2c4ea69b89cc2cb Mon Sep 17 00:00:00 2001 From: Adam Fowler Date: Sat, 13 Mar 2021 08:41:37 +0000 Subject: [PATCH 01/28] Modified tests to use catch2 INFO Signed-off-by: Adam Fowler --- herald-tests/blemacaddress-tests.cpp | 2 +- herald-tests/data-tests.cpp | 10 +++++----- herald-tests/datatypes-tests.cpp | 22 ++++++++++++++++++++- herald-tests/ranges-tests.cpp | 4 ++-- herald/include/herald/analysis/aggregates.h | 14 ++++++------- 5 files changed, 36 insertions(+), 16 deletions(-) diff --git a/herald-tests/blemacaddress-tests.cpp b/herald-tests/blemacaddress-tests.cpp index ebe5426..f7047ad 100644 --- a/herald-tests/blemacaddress-tests.cpp +++ b/herald-tests/blemacaddress-tests.cpp @@ -115,7 +115,7 @@ TEST_CASE("ble-macaddress-tostring", "[ble][macaddress][tostring]") { herald::ble::Data d(data,6); herald::ble::BLEMacAddress mac(d); std::string description = (std::string)mac; // conversion operator - std::cout << "BLEMacAddress: String description: " << description << std::endl; + INFO("BLEMacAddress: String description: " << description); REQUIRE(17 == description.size()); // 2 chars per 6 data elements, 5 : char separators REQUIRE("05:04:03:02:01:00" == description); // little endian conversion } diff --git a/herald-tests/data-tests.cpp b/herald-tests/data-tests.cpp index bc40d43..303b790 100644 --- a/herald-tests/data-tests.cpp +++ b/herald-tests/data-tests.cpp @@ -103,7 +103,7 @@ TEST_CASE("datatypes-data-from-uint8array", "[datatypes][data][ctor][from-uint8a herald::datatype::Data d{bytes, 4}; std::string hs = d.hexEncodedString(); - std::cout << "Data: uint8array as hexString: expected: 00010203, got: " << hs << std::endl; + INFO("Data: uint8array as hexString: expected: 00010203, got: " << hs); REQUIRE(d.size() == 4); REQUIRE("00010203" == hs); @@ -164,7 +164,7 @@ TEST_CASE("datatypes-data-ctor-fromhexstring", "[datatypes][data][ctor][fromhexs const std::string hex = "00010ff0ffcc"; herald::datatype::Data d = herald::datatype::Data::fromHexEncodedString(hex); const std::string finalhex = d.hexEncodedString(); - std::cout << "Data: fromHexEncodedString: from: " << hex << ", to: " << finalhex << std::endl; + INFO("Data: fromHexEncodedString: from: " << hex << ", to: " << finalhex); REQUIRE(d.size() == 6); REQUIRE(d.at(0) == std::byte(0)); @@ -265,7 +265,7 @@ TEST_CASE("datatypes-data-description", "[datatypes][data][description]") { herald::datatype::Data d{bytes, 4}; std::string hex = d.description(); - std::cout << "Data: description output: " << hex << std::endl; + INFO("Data: description output: " << hex); REQUIRE(hex.size() > 0); // NOTE: No requirements on format for this method - DO NOT rely on it } @@ -277,12 +277,12 @@ TEST_CASE("datatypes-data-hexencodedstring", "[datatypes][data][hexencodedstring herald::datatype::Data d{bytes, 4}; std::string hex = d.hexEncodedString(); - std::cout << "Data: hexEncodedString (std::string) output: " << hex << std::endl; + INFO("Data: hexEncodedString (std::string) output: " << hex); REQUIRE(8 == hex.size()); REQUIRE("00010203" == hex); std::string hexrev = d.reversed().hexEncodedString(); - std::cout << "Data: hexEncodedString (std::string) reversed output: " << hexrev << std::endl; + INFO("Data: hexEncodedString (std::string) reversed output: " << hexrev); REQUIRE(8 == hexrev.size()); REQUIRE("03020100" == hexrev); } diff --git a/herald-tests/datatypes-tests.cpp b/herald-tests/datatypes-tests.cpp index a0aedc1..26f32fd 100644 --- a/herald-tests/datatypes-tests.cpp +++ b/herald-tests/datatypes-tests.cpp @@ -337,7 +337,27 @@ TEST_CASE("datatypes-uuid-random","[randomness][uuid][basic][datatypes]") { herald::datatype::RandomnessGenerator gen(std::move(rnd)); auto randomV4 = herald::datatype::UUID::random(gen); std::string str = randomV4.string(); - std::cout << "UUID v4 random value: " << str << std::endl; + INFO("UUID v4 random value: " << str); REQUIRE(str != std::string("00000000-0000-4000-8000-000000000000")); // v4 variant 1 } +} + + + + +TEST_CASE("datatypes-memory-use","[datatypes][memory]") { + + SECTION("datatypes-memory-use") { + // TODO always output sizes to a CSV report file + + using namespace herald::datatype; + Base64String b64 = Base64String::encode(Data(std::byte(2),8)); + INFO("Base64String size for 8 chars: " << sizeof(b64)); + REQUIRE(sizeof(b64) <= 16); // 8 chars plus 64 bit size + Data d{std::byte(1),32}; + INFO("Data size for 32 bytes: " << sizeof(d)); + REQUIRE(sizeof(d) <= 40); + + // TODO other types here + } } \ No newline at end of file diff --git a/herald-tests/ranges-tests.cpp b/herald-tests/ranges-tests.cpp index c712049..6bf84d6 100644 --- a/herald-tests/ranges-tests.cpp +++ b/herald-tests/ranges-tests.cpp @@ -377,13 +377,13 @@ TEST_CASE("ranges-risk-aggregate", "[ranges][risk][aggregate][no-filter]") { if (firstNonZeroInterScore == 0.0 && interScore > 0) { firstNonZeroInterScore = interScore; } - std::cout << "RiskAggregationBasic inter score: " << interScore << " address of agg: " << &agg << std::endl; + INFO("RiskAggregationBasic inter score: " << interScore << " address of agg: " << &agg); } // Now we have the total for our 'whole contact duration', not scaled for how far in the past it is auto& agg = riskSlice.get(); double riskScore = agg.reduce(); - std::cout << "RiskAggregationBasic final score: " << riskScore << " address of agg: " << &agg << std::endl; + INFO("RiskAggregationBasic final score: " << riskScore << " address of agg: " << &agg); REQUIRE(interScore > 0.0); // final inter score should be non zero REQUIRE(riskScore > 0.0); // final score should be non zero REQUIRE(riskScore > firstNonZeroInterScore); // should be additive over time too diff --git a/herald/include/herald/analysis/aggregates.h b/herald/include/herald/analysis/aggregates.h index 0950c81..73c1cde 100644 --- a/herald/include/herald/analysis/aggregates.h +++ b/herald/include/herald/analysis/aggregates.h @@ -8,7 +8,7 @@ #include #include #include -#include +// #include #include "ranges.h" @@ -178,14 +178,14 @@ struct aggregate { for (auto& agg : me.aggregates) { std::visit([&run](auto&& arg) { arg.beginRun(run); - std::cout << "Beggining run " << run << std::endl; + // std::cout << "Beggining run " << run << std::endl; }, agg); } for (auto& v : from) { for (auto& agg : me.aggregates) { std::visit([&v](auto&& arg) { - std::cout << "Sample taken: " << v.taken.secondsSinceUnixEpoch() << std::endl; + // std::cout << "Sample taken: " << v.taken.secondsSinceUnixEpoch() << std::endl; arg.map(v); }, agg); } @@ -213,7 +213,7 @@ struct aggregate { for (auto& agg : me.aggregates) { std::visit([&run](auto&& arg) { arg.beginRun(run); - std::cout << "Beggining run " << run << std::endl; + // std::cout << "Beggining run " << run << std::endl; }, agg); } @@ -221,7 +221,7 @@ struct aggregate { auto& v = *from; for (auto& agg : me.aggregates) { std::visit([&v](auto&& arg) { - std::cout << "Sample taken: " << v.taken.secondsSinceUnixEpoch() << std::endl; + // std::cout << "Sample taken: " << v.taken.secondsSinceUnixEpoch() << std::endl; arg.map(v); }, agg); } @@ -250,14 +250,14 @@ struct aggregate { for (auto& agg : me.aggregates) { std::visit([&run](auto&& arg) { arg.beginRun(run); - std::cout << "Beggining run " << run << std::endl; + // std::cout << "Beggining run " << run << std::endl; }, agg); } for (auto& v : from) { for (auto& agg : me.aggregates) { std::visit([&v](auto&& arg) { - std::cout << "Sample taken: " << v.taken.secondsSinceUnixEpoch() << std::endl; + // std::cout << "Sample taken: " << v.taken.secondsSinceUnixEpoch() << std::endl; arg.map(v); }, agg); } From 326135856a27f8a0d79b81fc5eea8788d21ca297 Mon Sep 17 00:00:00 2001 From: Adam Fowler Date: Sat, 13 Mar 2021 21:04:00 +0000 Subject: [PATCH 02/28] In-progress data analysis runner design Signed-off-by: Adam Fowler --- herald-tests/CMakeLists.txt | 1 + herald-tests/analysisrunner-tests.cpp | 90 ++++++++++++++ herald/herald.cmake | 5 +- herald/include/herald.h | 4 +- herald/include/herald/analysis/distance.h | 58 --------- .../herald/analysis/distance_conversion.h | 104 ++++++++++++++++ herald/include/herald/analysis/runner.h | 43 +++++++ herald/include/herald/analysis/sampling.h | 72 +++++++---- herald/include/herald/datatype/distance.h | 44 +++++++ herald/src/datatype/distance.cpp | 112 ++++++++++++++++++ 10 files changed, 453 insertions(+), 80 deletions(-) create mode 100644 herald-tests/analysisrunner-tests.cpp delete mode 100644 herald/include/herald/analysis/distance.h create mode 100644 herald/include/herald/analysis/distance_conversion.h create mode 100644 herald/include/herald/analysis/runner.h create mode 100644 herald/include/herald/datatype/distance.h create mode 100644 herald/src/datatype/distance.cpp diff --git a/herald-tests/CMakeLists.txt b/herald-tests/CMakeLists.txt index f6faa21..b0d5c41 100644 --- a/herald-tests/CMakeLists.txt +++ b/herald-tests/CMakeLists.txt @@ -19,6 +19,7 @@ add_executable(herald-tests bledevice-tests.cpp sample-tests.cpp ranges-tests.cpp + analysisrunner-tests.cpp # high level advertparser-tests.cpp diff --git a/herald-tests/analysisrunner-tests.cpp b/herald-tests/analysisrunner-tests.cpp new file mode 100644 index 0000000..4af8701 --- /dev/null +++ b/herald-tests/analysisrunner-tests.cpp @@ -0,0 +1,90 @@ +// Copyright 2021 Herald project contributors +// SPDX-License-Identifier: Apache-2.0 +// + +#include "catch.hpp" + +#include "herald/herald.h" + +using namespace herald::analysis::sampling; +using namespace herald::datatype; + +template +struct DummyRSSISource { + DummyRssiSource(const std::size_t srcDeviceKey, const SampleList,Sz>& data) : key(srcDeviceKey), data(data) {}; + ~DummyRSSISource() = default; + + void run(AnalysisRunner& runner) { + // get reference to target list + auto& devList = runner.list(key); + // push through data at default rate + for (auto& v: data) { + devList.push(v.taken,v.value); // copy data over (It's unusual taking a SampleList and sending to a SampleList) + } + } + +private: + std::size_t key; + SampleList,Sz> data; +}; + +struct DummyDistanceDelegate { + DummyDistanceDelegate() = default; + ~DummyDistanceDelegate() = default; + + // Detected methods by AnalysisRunner + void newSampleArrived(SampledID sampled, Sample sample) { + lastSampledID = sampled; + distances.push(sample); + } + + void reset() { + distances.clear(); + lastSampledID = 0; + } + + // Test only methods + SampledID lastSampled() { + return lastSampledID; + } + + const SampleList,20>& samples() { + return distances; + } + +private: + SampledID lastSampledID; + SampleList,20> distances; +}; + +/// [Who] As a DCT app developer +/// [What] I want to link my live application data to an analysis runner easily +/// [Value] So I don't have to write plumbing code for Herald itself +/// +/// [Who] As a DCT app developer +/// [What] I want to periodically run analysis aggregates automatically +/// [Value] So I don't miss any information, and have accurate, regular, samples +TEST_CASE("analysisrunner-basic", "[analysisrunner][basic]") { + SECTION("analysisrunner-basic") { + SampleList,10> srcData{{10,-55},{20,-55},{30,-55},{40,-55},{50,-55},{60,-55},{70,-55},{80,-55},{90,-55},{100,-55}}; + DummyRSSISource src(srcData); + + herald::analysis::algorithms::distance::FowlerBasicAnalyser distanceAnalyser(30, -50, -24); + + herald::analysis::AnalysisRunner runner(distanceAnalyser); // std::moves and takes ownership + + DummyDistanceDelegate myDelegate; + runner.add(myDelegate); + + runner.run(); + + auto& samples = myDelegate.samples(); + REQUIRE(samples.size() == 3); // didn't reach 4x30 seconds, so no tenth sample + REQUIRE(samples[0].taken.secondsSinceUnixEpoch() == 30); + REQUIRE(samples[0].value != 0.0); + REQUIRE(samples[1].taken.secondsSinceUnixEpoch() == 60); + REQUIRE(samples[1].value != 0.0); + REQUIRE(samples[2].taken.secondsSinceUnixEpoch() == 90); + REQUIRE(samples[2].value != 0.0); + } +} \ No newline at end of file diff --git a/herald/herald.cmake b/herald/herald.cmake index 2f8d2a6..4288e72 100644 --- a/herald/herald.cmake +++ b/herald/herald.cmake @@ -10,7 +10,8 @@ set(HERALD_HEADERS ${HERALD_BASE}/include/herald/sensor_delegate.h ${HERALD_BASE}/include/herald/sensor.h ${HERALD_BASE}/include/herald/analysis/aggregates.h - ${HERALD_BASE}/include/herald/analysis/distance.h + ${HERALD_BASE}/include/herald/analysis/runner.h + ${HERALD_BASE}/include/herald/analysis/distance_conversion.h ${HERALD_BASE}/include/herald/analysis/ranges.h ${HERALD_BASE}/include/herald/analysis/risk.h ${HERALD_BASE}/include/herald/analysis/sampling.h @@ -38,6 +39,7 @@ set(HERALD_HEADERS ${HERALD_BASE}/include/herald/datatype/bluetooth_state.h ${HERALD_BASE}/include/herald/datatype/data.h ${HERALD_BASE}/include/herald/datatype/date.h + ${HERALD_BASE}/include/herald/datatype/distance.h ${HERALD_BASE}/include/herald/datatype/encounter.h ${HERALD_BASE}/include/herald/datatype/error_code.h ${HERALD_BASE}/include/herald/datatype/immediate_send_data.h @@ -91,6 +93,7 @@ set(HERALD_SOURCES ${HERALD_BASE}/src/datatype/base64_string.cpp ${HERALD_BASE}/src/datatype/data.cpp ${HERALD_BASE}/src/datatype/date.cpp + ${HERALD_BASE}/src/datatype/distance.cpp ${HERALD_BASE}/src/datatype/encounter.cpp ${HERALD_BASE}/src/datatype/immediate_send_data.cpp ${HERALD_BASE}/src/datatype/location.cpp diff --git a/herald/include/herald.h b/herald/include/herald.h index 52beb2b..ee1d7aa 100644 --- a/herald/include/herald.h +++ b/herald/include/herald.h @@ -28,6 +28,7 @@ #include "herald/datatype/bluetooth_state.h" #include "herald/datatype/data.h" #include "herald/datatype/date.h" +#include "herald/datatype/distance.h" #include "herald/datatype/encounter.h" #include "herald/datatype/error_code.h" #include "herald/datatype/immediate_send_data.h" @@ -80,7 +81,8 @@ // analysis namespace #include "herald/analysis/aggregates.h" -#include "herald/analysis/distance.h" +#include "herald/analysis/runner.h" +#include "herald/analysis/distance_conversion.h" #include "herald/analysis/ranges.h" #include "herald/analysis/risk.h" #include "herald/analysis/sampling.h" diff --git a/herald/include/herald/analysis/distance.h b/herald/include/herald/analysis/distance.h deleted file mode 100644 index ce123b9..0000000 --- a/herald/include/herald/analysis/distance.h +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright 2021 Herald Project Contributors -// SPDX-License-Identifier: Apache-2.0 -// - -#ifndef DISTANCE_H -#define DISTANCE_H - -#include - -#include "aggregates.h" -#include "ranges.h" - -namespace herald { -namespace analysis { -namespace algorithms { -namespace distance { - -using namespace herald::analysis::aggregates; - -struct FowlerBasic { - static constexpr int runs = 1; - - FowlerBasic(double intercept, double coefficient) : run(1), mode(), intercept(intercept), coefficient(coefficient) {} - ~FowlerBasic() = default; - - void beginRun(int thisRun) { // 1 indexed - run = thisRun; - mode.beginRun(thisRun); - } - - template - void map(ValT value) { - mode.map(value); - } - - double reduce() { - double exponent = (mode.reduce() - intercept) / coefficient; - return std::pow(10, exponent); // distance - } - - void reset() { - run = 1; - mode.reset(); - } - -private: - int run; - Mode mode; // cleaner to use the Mode rather than redo it in this class - double intercept; - double coefficient; -}; - -} -} -} -} - -#endif diff --git a/herald/include/herald/analysis/distance_conversion.h b/herald/include/herald/analysis/distance_conversion.h new file mode 100644 index 0000000..208418f --- /dev/null +++ b/herald/include/herald/analysis/distance_conversion.h @@ -0,0 +1,104 @@ +// Copyright 2021 Herald Project Contributors +// SPDX-License-Identifier: Apache-2.0 +// + +#ifndef DISTANCE_CONVERSION_H +#define DISTANCE_CONVERSION_H + +#include + +#include "aggregates.h" +#include "ranges.h" + +namespace herald { +namespace analysis { +namespace algorithms { +namespace distance { + +using namespace herald::analysis::aggregates; + +struct FowlerBasic { + static constexpr int runs = 1; + + FowlerBasic(double intercept, double coefficient) : run(1), mode(), intercept(intercept), coefficient(coefficient) {} + ~FowlerBasic() = default; + + void beginRun(int thisRun) { // 1 indexed + run = thisRun; + mode.beginRun(thisRun); + } + + template + void map(ValT value) { + mode.map(value); + } + + double reduce() { + double exponent = (mode.reduce() - intercept) / coefficient; + return std::pow(10, exponent); // distance + } + + void reset() { + run = 1; + mode.reset(); + } + +private: + int run; + Mode mode; // cleaner to use the Mode rather than redo it in this class + double intercept; + double coefficient; +}; + +struct FowlerBasicAnalyser { + FowlerBasicAnalyser(long interval, double intercept, double coefficient) : interval(interval), basic(intercept, coefficient) {} + ~FowlerBasicAnalyser() = default; + + template + void analyse(const SampleList,SrcSz>& src, SampleList,DstSz>& dst) { + basic.reset(); + + herald::analysis::views::in_range valid(-99,-10); + + using namespace herald::analysis::aggregates; + auto values = src + | herald::analysis::views::filter(valid) + | herald::analysis::views::to_view(); + + auto summary = values + | summarise(); + + auto mean = summary.get(); + auto mode = summary.get(); + auto var = summary.get(); + auto sd = std::sqrt(var); + + auto distance = sl + | herald::analysis::views::filter(valid) + | herald::analysis::views::filter( + herald::analysis::views::in_range( + mode - 2*sd, // NOTE: WE USE THE MODE FOR FILTER, BUT SD FOR BOUNDS - See website for the reasoning + mode + 2*sd + ) + ) + | aggregate(basic); // type actually + + auto agg = distance.get(); + auto d = agg.reduce(); + + Date latestTime = values.latest(); + + dst.push(latestTime,Distance(d)); + } + +private: + long interval; + FowlerBasic basic; +}; + +} +} +} +} + +#endif diff --git a/herald/include/herald/analysis/runner.h b/herald/include/herald/analysis/runner.h new file mode 100644 index 0000000..16ce83f --- /dev/null +++ b/herald/include/herald/analysis/runner.h @@ -0,0 +1,43 @@ +// Copyright 2021 Herald Project Contributors +// SPDX-License-Identifier: Apache-2.0 +// + +#ifndef ANALYSIS_RUNNER_H +#define ANALYSIS_RUNNER_H + +#include +#include + +namespace herald { +namespace analysis { + +template +class AnalysisRunner { +public: + static constexpr Size = sizeof...(AnalyserT); + + AnalysisRunner(AnalyserT... analyserList) : analysers() { + analysers.push(std::move(analyserList...)); + } + ~AnalysisRunner() = default; + + // Public methods here + void add(DistanceDelegate delegate) { + + } + + void run() { + + } + + template + SampleList& + +private: + std::array,Size> analysers; +}; + +} +} + +#endif \ No newline at end of file diff --git a/herald/include/herald/analysis/sampling.h b/herald/include/herald/analysis/sampling.h index 69ad515..9da701d 100644 --- a/herald/include/herald/analysis/sampling.h +++ b/herald/include/herald/analysis/sampling.h @@ -17,6 +17,12 @@ namespace sampling { using namespace herald::datatype; +/// The unique ID of a source instance that has been sampled. +/// E.g. 64 bit hash of some unique identifier in the source physical realm +/// (unknown to the analysis engine) +using SampledID = std::size_t; + +/// The Sample taken from an object with ID of type SampledID template struct Sample { using value_type = ValT; @@ -98,30 +104,23 @@ struct SampleList { static constexpr std::size_t max_size = MaxSize; - SampleList() : data(), oldestPosition(SIZE_MAX), newestPosition(SIZE_MAX) {}; + SampleList() : data(), oldestPosition(SIZE_MAX), newestPosition(SIZE_MAX) {} SampleList(const SampleList&) = delete; // no shallow copies allowed + + // Creates a list from static initialiser list elements + template + SampleList(Inits... initialiserElements) : data(), oldestPosition(SIZE_MAX), newestPosition(SIZE_MAX) { + appendData(initialiserElements); + } ~SampleList() = default; + void push(Sample sample) { + incrementNewest(); + data[newestPosition] = sample; + } + void push(Date taken, SampleValueT val) { - if (SIZE_MAX == newestPosition) { - newestPosition = 0; - oldestPosition = 0; - } else { - if (newestPosition == (oldestPosition - 1)) { - ++oldestPosition; - if (oldestPosition == data.size()) { - oldestPosition = 0; - } - } - ++newestPosition; - } - if (newestPosition == data.size()) { - // just gone past the end of the container - newestPosition = 0; - if (0 == oldestPosition) { - ++oldestPosition; // erases oldest if not already removed - } - } + incrementNewest(); data[newestPosition] = SampleT{taken,val}; } @@ -185,6 +184,39 @@ struct SampleList { std::array data; std::size_t oldestPosition; std::size_t newestPosition; + + void incrementNewest() { + if (SIZE_MAX == newestPosition) { + newestPosition = 0; + oldestPosition = 0; + } else { + if (newestPosition == (oldestPosition - 1)) { + ++oldestPosition; + if (oldestPosition == data.size()) { + oldestPosition = 0; + } + } + ++newestPosition; + } + if (newestPosition == data.size()) { + // just gone past the end of the container + newestPosition = 0; + if (0 == oldestPosition) { + ++oldestPosition; // erases oldest if not already removed + } + } + } + + template + void appendData(Inits first) { + push(first); + } + + template + void appendData(Inits first, Inits second, Inits... initialiserElements) { + push(first); + appendData(second, initialiserElements...); + } }; template + +namespace herald { +namespace datatype { + +/// Simple distance value in metres +/// Has to be a class/struct type to allow template resolution +struct Distance { + double value; + + Distance(); // default ctor (evaluates to 0) + Distance(double value); + Distance(const Distance& other); // copy ctor + Distance(Distance&& other); // move ctor + ~Distance(); + + Distance& operator=(const Distance& other); // copy assign + Distance& operator=(Distance&& other); // move assign + + bool operator==(const double other) const noexcept; + bool operator!=(const double other) const noexcept; + bool operator==(const Distance& other) const noexcept; + bool operator!=(const Distance& other) const noexcept; + bool operator<(const Distance& other) const noexcept; + bool operator<=(const Distance& other) const noexcept; + bool operator>(const Distance& other) const noexcept; + bool operator>=(const Distance& other) const noexcept; + + operator double() const noexcept; + + std::size_t hashCode() const noexcept; +}; + +} +} + +#endif \ No newline at end of file diff --git a/herald/src/datatype/distance.cpp b/herald/src/datatype/distance.cpp new file mode 100644 index 0000000..f096663 --- /dev/null +++ b/herald/src/datatype/distance.cpp @@ -0,0 +1,112 @@ +// Copyright 2021 Herald Project Contributors +// SPDX-License-Identifier: Apache-2.0 +// + +#include "herald/datatype/distance.h" + +namespace herald { +namespace datatype { + +Distance::Distance() : value(0.0) +{ + ; +} + +Distance::Distance(double value) : value(value) +{ + ; +} + +Distance::Distance(const Distance& other) + : value(other.value) +{ + ; +} + +Distance::Distance(Distance&& other) + : value(other.value) +{ + ; +} + +Distance::~Distance() = default; + +Distance& +Distance::operator=(const Distance& other) +{ + value = other.value; + return *this; +} + +Distance& +Distance::operator=(Distance&& other) +{ + value = other.value; + return *this; +} + +std::size_t +Distance::hashCode() const noexcept { + return std::hash{}(value); +} + +Distance::operator double() const noexcept { + return value; +} + + + +bool +Distance::operator==(const double other) const noexcept +{ + return value == other; +} + +bool +Distance::operator!=(const double other) const noexcept +{ + return value != other; +} + +bool +Distance::operator==(const Distance& other) const noexcept +{ + return value == other.value; +} + +bool +Distance::operator!=(const Distance& other) const noexcept +{ + return value != other.value; +} + +bool +Distance::operator<(const Distance& other) const noexcept +{ + return value < other.value; +} + +bool +Distance::operator<=(const Distance& other) const noexcept +{ + return value <= other.value; +} + +bool +Distance::operator>(const Distance& other) const noexcept +{ + return value > other.value; +} + +bool +Distance::operator>=(const Distance& other) const noexcept +{ + return value >= other.value; +} + + + + + +} // end namespace +} // end namespace From 4794950885d692d001878f3629897b8a7a9010be Mon Sep 17 00:00:00 2001 From: Adam Fowler Date: Sun, 14 Mar 2021 20:41:43 +0000 Subject: [PATCH 03/28] More work on analysis runner architecture Signed-off-by: Adam Fowler --- herald-tests/analysisrunner-tests.cpp | 38 ++-- herald-tests/sample-tests.cpp | 54 ++++++ .../herald/analysis/distance_conversion.h | 24 ++- herald/include/herald/analysis/runner.h | 173 ++++++++++++++++-- herald/include/herald/analysis/sampling.h | 35 ++-- herald/src/datatype/distance.cpp | 2 + 6 files changed, 282 insertions(+), 44 deletions(-) diff --git a/herald-tests/analysisrunner-tests.cpp b/herald-tests/analysisrunner-tests.cpp index 4af8701..3cf59da 100644 --- a/herald-tests/analysisrunner-tests.cpp +++ b/herald-tests/analysisrunner-tests.cpp @@ -11,16 +11,23 @@ using namespace herald::datatype; template struct DummyRSSISource { - DummyRssiSource(const std::size_t srcDeviceKey, const SampleList,Sz>& data) : key(srcDeviceKey), data(data) {}; + using value_type = Sample; // allows AnalysisRunner to introspect this class at compile time + + DummyRSSISource(const std::size_t srcDeviceKey, SampleList,Sz>&& data) : key(srcDeviceKey), data(std::move(data)) {}; ~DummyRSSISource() = default; - void run(AnalysisRunner& runner) { + template + void run(int timeTo, RunnerT& runner) { // get reference to target list - auto& devList = runner.list(key); + // auto& devList = runner.list(key); // push through data at default rate for (auto& v: data) { - devList.push(v.taken,v.value); // copy data over (It's unusual taking a SampleList and sending to a SampleList) + // devList.push(v.taken,v.value); // copy data over (It's unusual taking a SampleList and sending to a SampleList) + if (v.taken.secondsSinceUnixEpoch() <= timeTo) { + runner.newSample<20>(key,v); + } } + runner.run(timeTo); } private: @@ -33,7 +40,7 @@ struct DummyDistanceDelegate { ~DummyDistanceDelegate() = default; // Detected methods by AnalysisRunner - void newSampleArrived(SampledID sampled, Sample sample) { + void newSample(SampledID sampled, Sample sample) { lastSampledID = sampled; distances.push(sample); } @@ -66,17 +73,26 @@ struct DummyDistanceDelegate { /// [Value] So I don't miss any information, and have accurate, regular, samples TEST_CASE("analysisrunner-basic", "[analysisrunner][basic]") { SECTION("analysisrunner-basic") { - SampleList,10> srcData{{10,-55},{20,-55},{30,-55},{40,-55},{50,-55},{60,-55},{70,-55},{80,-55},{90,-55},{100,-55}}; - DummyRSSISource src(srcData); + SampleList,10> srcData{ + Sample{10,-55},Sample{20,-55},Sample{30,-55},Sample{40,-55},Sample{50,-55}, + Sample{60,-55},Sample{70,-55},Sample{80,-55},Sample{90,-55},Sample{100,-55} + }; + DummyRSSISource src(1234,std::move(srcData)); herald::analysis::algorithms::distance::FowlerBasicAnalyser distanceAnalyser(30, -50, -24); - herald::analysis::AnalysisRunner runner(distanceAnalyser); // std::moves and takes ownership + herald::analysis::AnalysisRunner runner; // just for Sample types, and their produced output (Sample) DummyDistanceDelegate myDelegate; - runner.add(myDelegate); - - runner.run(); + runner.add(distanceAnalyser); // so it picks up new data items + runner.add(myDelegate); // so we receive the relevant output + + // run at different times and ensure that it only actually runs three times (sample size == 3) + src.run(20,runner); + src.run(40,runner); + src.run(60,runner); + src.run(80,runner); + src.run(95,runner); auto& samples = myDelegate.samples(); REQUIRE(samples.size() == 3); // didn't reach 4x30 seconds, so no tenth sample diff --git a/herald-tests/sample-tests.cpp b/herald-tests/sample-tests.cpp index abaa074..7923e42 100644 --- a/herald-tests/sample-tests.cpp +++ b/herald-tests/sample-tests.cpp @@ -494,3 +494,57 @@ TEST_CASE("samplelist-iterator-cleared", "[samplelist][iterator][cleared]") { } // Now handle other container functionality required + +TEST_CASE("sample-init-list", "[sample][initialiser][list]") { + SECTION("sample-init-list") { + herald::analysis::sampling::Sample sample{10,-55}; + REQUIRE(sample.taken.secondsSinceUnixEpoch() == 10); + REQUIRE(sample.value == -55); + } +} + +TEST_CASE("samplelist-init-list", "[samplelist][initialiser][list]") { + SECTION("samplelist-init-list") { + herald::analysis::sampling::Sample sample1{10,-55}; + herald::analysis::sampling::Sample sample2{20,-65}; + herald::analysis::sampling::Sample sample3{30,-75}; + herald::analysis::sampling::SampleList,3> sl{sample1,sample2,sample3}; + REQUIRE(sl[0].taken.secondsSinceUnixEpoch() == 10); + REQUIRE(sl[0].value == -55); + REQUIRE(sl[1].taken.secondsSinceUnixEpoch() == 20); + REQUIRE(sl[1].value == -65); + REQUIRE(sl[2].taken.secondsSinceUnixEpoch() == 30); + REQUIRE(sl[2].value == -75); + } +} + +TEST_CASE("samplelist-init-list-deduced", "[samplelist][initialiser][list][deduced]") { + SECTION("samplelist-init-list-deduced") { + herald::analysis::sampling::Sample sample1{10,-55}; + herald::analysis::sampling::Sample sample2{20,-65}; + herald::analysis::sampling::Sample sample3{30,-75}; + herald::analysis::sampling::SampleList sl{sample1,sample2,sample3}; // full type is deduced + REQUIRE(sl[0].taken.secondsSinceUnixEpoch() == 10); + REQUIRE(sl[0].value == -55); + REQUIRE(sl[1].taken.secondsSinceUnixEpoch() == 20); + REQUIRE(sl[1].value == -65); + REQUIRE(sl[2].taken.secondsSinceUnixEpoch() == 30); + REQUIRE(sl[2].value == -75); + } +} + +TEST_CASE("samplelist-init-list-alldeduced", "[samplelist][initialiser][list][alldeduced]") { + SECTION("samplelist-init-list-alldeduced") { + herald::analysis::sampling::SampleList,3> sl{ + herald::analysis::sampling::Sample{10,-55}, + herald::analysis::sampling::Sample{20,-65}, + herald::analysis::sampling::Sample{30,-75} + }; + REQUIRE(sl[0].taken.secondsSinceUnixEpoch() == 10); + REQUIRE(sl[0].value == -55); + REQUIRE(sl[1].taken.secondsSinceUnixEpoch() == 20); + REQUIRE(sl[1].value == -65); + REQUIRE(sl[2].taken.secondsSinceUnixEpoch() == 30); + REQUIRE(sl[2].value == -75); + } +} \ No newline at end of file diff --git a/herald/include/herald/analysis/distance_conversion.h b/herald/include/herald/analysis/distance_conversion.h index 208418f..7fe0f55 100644 --- a/herald/include/herald/analysis/distance_conversion.h +++ b/herald/include/herald/analysis/distance_conversion.h @@ -9,6 +9,9 @@ #include "aggregates.h" #include "ranges.h" +#include "runner.h" +#include "sampling.h" +#include "../datatype/distance.h" namespace herald { namespace analysis { @@ -16,6 +19,8 @@ namespace algorithms { namespace distance { using namespace herald::analysis::aggregates; +using namespace herald::analysis::sampling; +using namespace herald::datatype; struct FowlerBasic { static constexpr int runs = 1; @@ -51,24 +56,24 @@ struct FowlerBasic { }; struct FowlerBasicAnalyser { - FowlerBasicAnalyser(long interval, double intercept, double coefficient) : interval(interval), basic(intercept, coefficient) {} + FowlerBasicAnalyser(long interval, double intercept, double coefficient) : interval(interval), basic(intercept, coefficient), lastRan(0) {} ~FowlerBasicAnalyser() = default; - template - void analyse(const SampleList,SrcSz>& src, SampleList,DstSz>& dst) { + template + void analyse(Date timeNow, const SampleList,SrcSz>& src, analysis::AnalysisDelegate& dst) { + if (lastRan + interval > timeNow) return; // interval guard + basic.reset(); - + herald::analysis::views::in_range valid(-99,-10); - using namespace herald::analysis::aggregates; auto values = src | herald::analysis::views::filter(valid) | herald::analysis::views::to_view(); auto summary = values - | summarise(); + | summarise(); - auto mean = summary.get(); auto mode = summary.get(); auto var = summary.get(); auto sd = std::sqrt(var); @@ -83,17 +88,18 @@ struct FowlerBasicAnalyser { ) | aggregate(basic); // type actually - auto agg = distance.get(); + auto agg = distance.get(); auto d = agg.reduce(); Date latestTime = values.latest(); - dst.push(latestTime,Distance(d)); + dst.newSample(Sample(latestTime,Distance(d))); } private: long interval; FowlerBasic basic; + Date lastRan; }; } diff --git a/herald/include/herald/analysis/runner.h b/herald/include/herald/analysis/runner.h index 16ce83f..126448b 100644 --- a/herald/include/herald/analysis/runner.h +++ b/herald/include/herald/analysis/runner.h @@ -5,38 +5,185 @@ #ifndef ANALYSIS_RUNNER_H #define ANALYSIS_RUNNER_H +#include "sampling.h" + #include #include namespace herald { namespace analysis { -template -class AnalysisRunner { -public: - static constexpr Size = sizeof...(AnalyserT); +using namespace sampling; + +// FWD Alias Declaration, not definition +template +struct AnalysisDelegate { + AnalysisDelegate(Destination& dst) : destination(dst) {} + ~AnalysisDelegate() = default; + + void newSample(SampledID sampled, Sample sample); + + template + void setDestination(RunnerT& runner); + + Destination& destination; +}; + +/// Manages a set of lists for a particular Sample Value Type +template +struct ListManager { + using value_type = ValT; + + ListManager() = default; + ~ListManager() = default; - AnalysisRunner(AnalyserT... analyserList) : analysers() { - analysers.push(std::move(analyserList...)); + auto& list(const SampledID sampled) { + return lists.emplace(sampled).first; } - ~AnalysisRunner() = default; - // Public methods here - void add(DistanceDelegate delegate) { + void remove(const SampledID listFor) { + lists.erase(listFor); + } + +private: + std::map,Size>> lists; +}; + +/// A fixed size set that holds exactly one instance of the std::variant for each +/// of the specified ValTs value types. +template +struct VariantSet { + static constexpr std::size_t Size = sizeof...(ValTs); + + VariantSet() = default; + ~VariantSet() = default; + /// CAN THROW std::bad_variant_access + template + ValT& get() { + for (auto& v : variants) { + if (auto pval = std::get_if(&v)) { + return *pval; + } + } + throw std::bad_variant_access(); } - void run() { +private: + std::array,Size> variants; +}; + +/// The below is an example ValueSource... +/// template +/// struct ExValueSource { +/// using value_type = ValT; +/// +/// template +/// void setDestination(RunnerT& runner) { +/// // save reference +/// } +/// +/// // At some predetermined point external to the analyser runner +/// // this ValueSource will call runner.newSample(Sample sample) +/// }; +template +struct AnalysisRunner { + // using valueTypes = (typename SourceTypes::value_type)...; + + AnalysisRunner(/*SourceTypes&... sources*/) : lists(), notifiables() { + // registerRunner(sources...); + } + ~AnalysisRunner() = default; + + /// We are an analysis delegate ourselves - this is used by Source types, and by producers (analysis runners) + template + void newSample(SampledID sampled, sampling::Sample sample) { + // incoming sample. Pass to correct list + lists.get>().list(sampled).push(sample); } - template - SampleList& + /// Run the relevant analyses given the current time point + void run(Date timeNow) { + // call analyse(dateNow,srcList,dstDelegate) for all delegates with the correct list each, for each sampled + // TODO performance enhancement - 'dirty' sample lists only (ones with new data) + for (auto& listManager : lists) { + using ValT = listManager::value_type; + for (auth& delegate : notifiables) { + if constexpr (std::is_same_v) { // SHOULD BE RUNNERS NOT DELEGATES + delegate. + } + } + } + } + + template // TODO restrict this to one of the SourceTypes... types + void add(Destination& delegate) { + notifiables.push_back(std::variant...>(delegate)); + } + + // /// callback from analysis data source + // template + // SampleList>& list(const SampledID sampled) { + // return lists.get().list(sampled); + // } private: - std::array,Size> analysers; + // TODO make sizes a parameterised list derived from template parameters + VariantSet...> lists; // exactly one per value type + std::vector...>> notifiables; // more than one per value type + std::vector< // runners + + template + void registerRunner(FirstT first) { + first.setDestination(*this); + } + + template + void registerRunner(FirstT first,SecondT second,RestT... rest) { + first.setDestination(*this); + registerRunner(second,rest...); + } }; + + + + + + + + +// template +// class AnalysisRunner { +// public: +// static constexpr std::size_t Size = sizeof...(AnalyserT); + +// AnalysisRunner(AnalyserT... analyserList) : analysers() { +// analysers.push(std::move(analyserList...)); +// } +// ~AnalysisRunner() = default; + +// // Public methods here +// template +// void add(AnalysisDelegate delegate) { + +// } + +// void run() { + +// } + +// // template +// SampleList& list(sampling::SampledID sampled) { + +// } + +// private: +// std::array,Size> analysers; +// std::map< +// }; + } } diff --git a/herald/include/herald/analysis/sampling.h b/herald/include/herald/analysis/sampling.h index 9da701d..d1d63c2 100644 --- a/herald/include/herald/analysis/sampling.h +++ b/herald/include/herald/analysis/sampling.h @@ -5,10 +5,11 @@ #ifndef SAMPLING_H #define SAMPLING_H +#include "../datatype/date.h" + #include #include - -#include "../datatype/date.h" +#include namespace herald { namespace analysis { @@ -34,6 +35,8 @@ struct Sample { Sample(Date sampled, ValT v) : taken(Date{sampled.secondsSinceUnixEpoch()}), value(v) {} Sample(const Sample& other) : taken(Date{other.taken.secondsSinceUnixEpoch()}), value(other.value) {} // copy ctor Sample(Sample&& other) : taken(std::move(other.taken)), value(std::move(other.value)) {} // move ctor + template + Sample(int secondsSinceEpoch,Args... args) : taken(Date(secondsSinceEpoch)), value(ValT(args...)) {} // initialiser list constructor ~Sample() = default; Sample& operator=(Sample&& other) { @@ -106,12 +109,17 @@ struct SampleList { SampleList() : data(), oldestPosition(SIZE_MAX), newestPosition(SIZE_MAX) {} SampleList(const SampleList&) = delete; // no shallow copies allowed + SampleList(SampleList&& other) : data(std::move(other.data)), oldestPosition(other.oldestPosition), newestPosition(other.newestPosition) {} // move ctor - // Creates a list from static initialiser list elements - template - SampleList(Inits... initialiserElements) : data(), oldestPosition(SIZE_MAX), newestPosition(SIZE_MAX) { - appendData(initialiserElements); + // Creates a list from static initialiser list elements, using deduction guide + template + SampleList(MultiSampleT... initialiserElements) : data(), oldestPosition(SIZE_MAX), newestPosition(SIZE_MAX) { + appendData(initialiserElements...); } + // This one requires specified final type, but deduces constructor to use + // SampleList(SampleT... initialiserElements) : data(), oldestPosition(SIZE_MAX), newestPosition(SIZE_MAX) { + // appendData(initialiserElements...); + // } ~SampleList() = default; void push(Sample sample) { @@ -207,17 +215,22 @@ struct SampleList { } } - template - void appendData(Inits first) { - push(first); + template + void appendData(LastT last) { + push(last); } - template - void appendData(Inits first, Inits second, Inits... initialiserElements) { + template + void appendData(FirstT first, SecondT second, Inits... initialiserElements) { push(first); appendData(second, initialiserElements...); } }; +// Deduction guides +template , typename CommonValTValue = typename CommonValT::value_type> +SampleList(ValT... valueList) -> SampleList; +// template +// SampleList(Sample... valueList) -> SampleList,sizeof...(valueList),SampleValueT>; // TODO figure out guide when we know it's a Sample template // from fwd decl => = typename SampleListT::value_type diff --git a/herald/src/datatype/distance.cpp b/herald/src/datatype/distance.cpp index f096663..c2a44e1 100644 --- a/herald/src/datatype/distance.cpp +++ b/herald/src/datatype/distance.cpp @@ -4,6 +4,8 @@ #include "herald/datatype/distance.h" +#include + namespace herald { namespace datatype { From 3c4e2c40617ee9dac4902331098cafc1670d99ed Mon Sep 17 00:00:00 2001 From: Adam Fowler Date: Sun, 14 Mar 2021 21:35:34 +0000 Subject: [PATCH 04/28] Initial Doxygen documentation Signed-off-by: Adam Fowler --- CMakeLists.txt | 3 +- doxygen/CMakeLists.txt | 11 +++++ herald/include/herald.h | 7 +++- herald/include/herald/context.h | 18 ++++---- .../include/herald/datatype/base64_string.h | 16 ++++++- herald/include/herald/datatype/data.h | 10 ++++- .../include/herald/default_sensor_delegate.h | 4 +- herald/include/herald/device.h | 16 ++++--- herald/include/herald/engine/activities.h | 42 ++++++++++++++----- herald/include/herald/engine/coordinator.h | 40 ++++++++++-------- herald/include/herald/sensor.h | 8 +++- herald/include/herald/sensor_array.h | 14 ++++++- herald/include/herald/sensor_delegate.h | 1 + herald/include/herald/zephyr_context.h | 8 ++-- 14 files changed, 145 insertions(+), 53 deletions(-) create mode 100644 doxygen/CMakeLists.txt diff --git a/CMakeLists.txt b/CMakeLists.txt index 004ac74..e879025 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -18,4 +18,5 @@ add_subdirectory(heraldns-cli) add_subdirectory(herald) add_subdirectory(herald-tests) add_subdirectory(herald-programmer) -add_subdirectory(herald-mesh-proxy) \ No newline at end of file +add_subdirectory(herald-mesh-proxy) +add_subdirectory(doxygen) \ No newline at end of file diff --git a/doxygen/CMakeLists.txt b/doxygen/CMakeLists.txt new file mode 100644 index 0000000..4211e70 --- /dev/null +++ b/doxygen/CMakeLists.txt @@ -0,0 +1,11 @@ +cmake_minimum_required(VERSION 3.12) + +find_package(Doxygen + OPTIONAL_COMPONENTS dot mscgen dia) + +doxygen_add_docs(herald-docs + ../herald/include + ALL +) + +# Output placed in herald-for-cpp/build/doxygen/html diff --git a/herald/include/herald.h b/herald/include/herald.h index ee1d7aa..e979db0 100644 --- a/herald/include/herald.h +++ b/herald/include/herald.h @@ -1,4 +1,4 @@ -// Copyright 2020 VMware, Inc. +// Copyright 2020-2021 Herald Project Contributors // SPDX-License-Identifier: Apache-2.0 // @@ -103,3 +103,8 @@ #include "herald/payload/extended/extended_data.h" // service namespace + +/// \brief The main Herald Proximity namespace in C++ +namespace herald { + +} \ No newline at end of file diff --git a/herald/include/herald/context.h b/herald/include/herald/context.h index fd3626b..97ff8ca 100644 --- a/herald/include/herald/context.h +++ b/herald/include/herald/context.h @@ -21,11 +21,13 @@ class SensorLoggingSink; // fwd decl using namespace herald::data; -/** - * Some platforms require global configuration or static configuration that - * doesn't map well on to C++ idioms. This class provides an extension capability - * to allow this linking. - */ +/// +/// \brief High level abstraction to access platform-specific implemented primitives. +/// +/// Some platforms require global configuration or static configuration that +/// doesn't map well on to C++ idioms. This class provides an extension capability +/// to allow this linking. +/// class Context { public: Context() = default; @@ -35,9 +37,9 @@ class Context { virtual std::shared_ptr getBluetoothStateManager() = 0; }; -/** - * Default context that just sends logging to stdout - */ +/// +/// \brief Default context that just sends logging to stdout +/// class DefaultContext : public Context { public: DefaultContext() = default; diff --git a/herald/include/herald/datatype/base64_string.h b/herald/include/herald/datatype/base64_string.h index a69d00a..c24aaba 100644 --- a/herald/include/herald/datatype/base64_string.h +++ b/herald/include/herald/datatype/base64_string.h @@ -1,4 +1,4 @@ -// Copyright 2020 VMware, Inc. +// Copyright 2020-2021 Herald Project Contributors // SPDX-License-Identifier: Apache-2.0 // @@ -10,22 +10,34 @@ #include namespace herald { +/// \brief Contains all low-level Herald datatype implementations namespace datatype { -/// Strongly, rather than stringly, typed representation of Base64 String data. +/// \brief Strongly, rather than stringly, typed representation of Base64 String data. /// Prevents incorrect initialisation or manipulation class Base64String { public: Base64String(); // empty string initialiser + /// \brief Move Constructor Base64String(Base64String&& other); + /// \brief Deleted copy constructor to prevent temporary memory use + Base64String(const Base64String& other) = delete; + /// \brief Custom destructor ~Base64String(); + /// \brief Populates a Base64String from a normal std::string static bool from(const std::string& original, Base64String& toInitialise) noexcept; // initialise from string + /// \brief Creates a Base64String from an arbitrary set of bytes + /// \sa Data static Base64String encode(const Data& from) noexcept; // initialise from Data + /// \brief Decodes this Base64String's content into a Data instance + /// \sa Data Data decode() const noexcept; + /// \brief Returns the Base64 encoding of this class as a std::string std::string encoded() const noexcept; // Return base64 string representation (copy of, not reference to) private: + /// \brief PIMPL Idiom - hidden internal class for Base64String for ABI compatibility class Impl; std::unique_ptr mImpl; // PIMPL IDIOM }; diff --git a/herald/include/herald/datatype/data.h b/herald/include/herald/datatype/data.h index fc0f8f8..ce7a0de 100644 --- a/herald/include/herald/datatype/data.h +++ b/herald/include/herald/datatype/data.h @@ -1,4 +1,4 @@ -// Copyright 2020 VMware, Inc. +// Copyright 2020-2021 Herald Project Contributors // SPDX-License-Identifier: Apache-2.0 // @@ -13,6 +13,14 @@ namespace herald { namespace datatype { +/// \brief The main data workhorse class of the Herald API +/// +/// A vitally important part of Herald's datatypes. Many other types +/// are actually just aliases or thin wrappers to the Data class. +/// +/// This class represents an arbitrarily long Big Endian list of std::byte. +/// Data instances are used to encode Bluetooth advert data, to pass payloads +/// between herald enabled devices, or to share data to and from backend systems. class Data { public: Data(); diff --git a/herald/include/herald/default_sensor_delegate.h b/herald/include/herald/default_sensor_delegate.h index ba4c4f5..94c4dcc 100644 --- a/herald/include/herald/default_sensor_delegate.h +++ b/herald/include/herald/default_sensor_delegate.h @@ -1,4 +1,4 @@ -// Copyright 2020 VMware, Inc. +// Copyright 2020-2021 Herald Project Contributors // SPDX-License-Identifier: Apache-2.0 // @@ -11,6 +11,8 @@ namespace herald { +/// \brief Default implementation that provides implementations for each delegate callback. +/// \sa SensorDelegate class DefaultSensorDelegate : public SensorDelegate { public: DefaultSensorDelegate(); diff --git a/herald/include/herald/device.h b/herald/include/herald/device.h index ddd0eb5..8ebbe92 100644 --- a/herald/include/herald/device.h +++ b/herald/include/herald/device.h @@ -1,4 +1,4 @@ -// Copyright 2020 VMware, Inc. +// Copyright 2020-2021 Herald Project Contributors // SPDX-License-Identifier: Apache-2.0 // @@ -14,10 +14,16 @@ namespace herald { using namespace herald::datatype; -/** - * Only implemented in final version to allow TimeInterval and other - * potentially platform specific implementation details to be overridden - */ +/// +/// \brief Generic abstraction of a particular local proximate device type. +/// +/// Could be a Bluetooth Low Energy device (BLEDevice) or some other technology. +/// +/// Only implemented in final version to allow TimeInterval and other +/// potentially platform specific implementation details to be overridden. +/// +/// \sa herald::ble::BLEDevice +/// class Device { public: Device() = default; diff --git a/herald/include/herald/engine/activities.h b/herald/include/herald/engine/activities.h index 015ef9a..95e5180 100644 --- a/herald/include/herald/engine/activities.h +++ b/herald/include/herald/engine/activities.h @@ -21,14 +21,22 @@ using namespace herald::datatype; // Types used in coordination of Herald sensor activities +/// \brief Herald implementation provided Feature tag/identifier using FeatureTag = herald::datatype::Data; +/// \brief Lists all Features currently supported by Herald providers namespace Features { + /// \brief Herald Bluetooth protocol connection is the first supported dependency type. static FeatureTag HeraldBluetoothProtocolConnection = herald::datatype::Data(std::byte(0x01),1); } +/// \brief A relative priority between different activities using Priority = std::uint8_t; +/// \brief Convenience Priority values +/// +/// Try to not use the same value, but rather offset by 5 or 10 between different dependencies. +/// Be sure to avoid circular priority dependencies. namespace Priorities { constexpr Priority Critical(200); constexpr Priority High(150); @@ -38,7 +46,11 @@ namespace Priorities { struct Activity; // fwd decl +/// \brief An absolute prerequisite required before an activity can take place +/// +/// An example would be the presence of a Bluetooth connection to a specified Device. using Prerequisite = std::tuple>; +/// \brief a Presrequisite with a relative priority assigned to assist Herald to prioritise effectively. using PrioritisedPrerequisite = std::tuple>; @@ -52,40 +64,50 @@ using PrioritisedPrerequisite = std::tuple; // function by value -// THE FOLLOWING IS FOR PLATFORMS WITHOUT STD::ASYNC SUPPORT (i.e. that is SYNC ONLY) +// THE FOLLOWING IS FOR PLATFORMS WITHOUT STD::ASYNC SUPPORT (i.e. that are SYNC ONLY) +/// \brief A convenience Function alias that invokes an activity and optionally returns a follow-on activity. using ActivityFunction = std::function(const Activity)>; // END RESULTS ONLY PLATFORMS +/// \brief An activity that needs to be performed due to some state being achieved in a Sensor struct Activity { + /// \brief The relative priority for this Activity compared to the scale + /// \sa Priorities Priority priority; + /// \brief A human readable name for this activity used for logging. std::string name; + /// \brief A list of non-prioritised pre-requisities (priority is taken from the priority field in Activity). std::vector prerequisites; // no target id means all that are connected + /// \brief The Activity function to call when all prerequisites have been met. May not be called. ActivityFunction executor; }; -/** - * Some sensors may have dependencies on others, or system features. - * This class provides a way of Sensors to let the Herald system know - * of their requirements and capabilities at any given moment. - */ +/// \brief Coordination management class that arranges Sensor's periodic requirements and activity interdependencies. +/// +/// Some sensors may have dependencies on others, or system features. +/// This class provides a way of Sensors to let the Herald system know +/// of their requirements and capabilities at any given moment. +/// class CoordinationProvider { public: CoordinationProvider() = default; virtual ~CoordinationProvider() = default; // Coordination methods - Since v1.2-beta3 - /** What connections does this Sensor type provide for Coordination **/ + /// What connections does this Sensor type provide for Coordination virtual std::vector connectionsProvided() = 0; - // Runtime connection provisioning (if it isn't requested, it can be closed) **/ - // WITH STD::SYNC ONLY: virtual void provision(const std::vector& requested, const ConnectionCallback& connCallback) = 0; + /// \brief Runtime connection provisioning (if it isn't requested, it can be closed) + /// + /// Note: WITH STD::SYNC ONLY: virtual void provision(const std::vector& requested, const ConnectionCallback& connCallback) = 0; virtual std::vector provision(const std::vector& requested) = 0; // Runtime coordination callbacks - /** Get a list of what connections are required to which devices now (may start, maintain, end (if not included)) **/ + /// \brief Get a list of what connections are required to which devices now (may start, maintain, end (if not included)) virtual std::vector requiredConnections() = 0; + /// \brief Get a list of activities that are currently outstanding in this iteration virtual std::vector requiredActivities() = 0; }; diff --git a/herald/include/herald/engine/coordinator.h b/herald/include/herald/engine/coordinator.h index 66ae810..c2a60ee 100644 --- a/herald/include/herald/engine/coordinator.h +++ b/herald/include/herald/engine/coordinator.h @@ -11,35 +11,41 @@ #include namespace herald { + +/// \brief Engine classes provide for task scheduling, including complex inter-dependent tasks. namespace engine { -/** - * Coordinates all connection and activities used across all sensors within Herald - * - * Responsible for:- - * - Determining Sensor capabilities and requirements around connections and activities - * - * Manages:- - * - Nothing, but coordinates activities throughout Herald Sensor networks on behalf of Sensor Array - * - * Is managed by:- - * - Sensor Array - */ +/// +/// \brief Coordinates all connection and activities used across all sensors within Herald +/// +/// Responsible for:- +/// +/// - Determining Sensor capabilities and requirements around connections and Activity instances +/// +/// Manages:- +/// +/// - Nothing, but coordinates activities throughout Herald Sensor networks on behalf of SensorArray +/// +/// Is managed by:- +/// +/// - SensorArray +/// class Coordinator { public: + /// Default constructor. Receives a configured platform-specific context instance. Coordinator(std::shared_ptr context); ~Coordinator(); - /** Introspect and include in iteration planning **/ + /// Introspect and include in iteration planning void add(std::shared_ptr sensor); - /** Remove from iteration planning **/ + /// Remove from iteration planning void remove(std::shared_ptr sensor); - /** Prepares for iterations to be called (may pre-emptively make calls) **/ + /// Prepares for iterations to be called (may pre-emptively make calls) void start(); - /** Execute an iteration of activity, according to settings **/ + /// Execute an iteration of activity, according to settings void iteration(); - /** Closes out any existing connections/activities **/ + /// Closes out any existing connections/activities void stop(); private: diff --git a/herald/include/herald/sensor.h b/herald/include/herald/sensor.h index 24d52e4..74503af 100644 --- a/herald/include/herald/sensor.h +++ b/herald/include/herald/sensor.h @@ -1,4 +1,4 @@ -// Copyright 2020 VMware, Inc. +// Copyright 2020-2021 Herald Project Contributors // SPDX-License-Identifier: Apache-2.0 // @@ -12,6 +12,10 @@ namespace herald { using namespace herald::engine; +/// \brief The base Sensor class that all Sensors implement. +/// +/// A Sensor could be a bluetooth transmitter or receiver (scanner), or an NFC +/// receiver, or some other proximity sensor (E.g. UWB radio). class Sensor { public: Sensor() = default; @@ -21,7 +25,7 @@ class Sensor { virtual void start() = 0; virtual void stop() = 0; - /** For complex sensor coordination support, if required - Since v1.2-beta3 **/ + /// \brief For complex sensor coordination support, if required - Since v1.2-beta3 virtual std::optional> coordinationProvider() = 0; }; diff --git a/herald/include/herald/sensor_array.h b/herald/include/herald/sensor_array.h index d9b04bd..9ef108a 100644 --- a/herald/include/herald/sensor_array.h +++ b/herald/include/herald/sensor_array.h @@ -21,9 +21,19 @@ namespace herald { using namespace datatype; using namespace payload; +/// \brief Manages all Sensors and sensor delegates for Herald +/// +/// This is the Core logic and runtime class for all of Herald. +/// It is platform independent and Sensor technology independent. +/// On C++ this class does NOT use threads. It is instead up to +/// the platform developer to call the iteration(TimeInterval) +/// function from whichever scheduling or threading tools are +/// available on each platform. In Zephyr RTOS, for example, +/// This is a simple 250ms delay within a special Herald-only +/// Zephyr kernel thread. class SensorArray : public Sensor { public: - /// Takes ownership of payloadDataSupplier (std::move) + /// \brief Takes ownership of payloadDataSupplier (std::move) SensorArray(std::shared_ptr ctx, std::shared_ptr payloadDataSupplier); ~SensorArray(); @@ -39,7 +49,7 @@ class SensorArray : public Sensor { void stop() override; std::optional> coordinationProvider() override; - // Scheduling activities from external OS thread wakes - Since v1.2-beta3 + /// \brief Scheduling activities from external OS thread wakes - Since v1.2-beta3 void iteration(const TimeInterval sinceLastCompleted); private: diff --git a/herald/include/herald/sensor_delegate.h b/herald/include/herald/sensor_delegate.h index 373422a..69b093a 100644 --- a/herald/include/herald/sensor_delegate.h +++ b/herald/include/herald/sensor_delegate.h @@ -19,6 +19,7 @@ namespace herald { using namespace datatype; +/// \brief Base interface for classes wishing to implement callbacks for core low-level Herald proximity and presence events. class SensorDelegate { public: SensorDelegate() = default; diff --git a/herald/include/herald/zephyr_context.h b/herald/include/herald/zephyr_context.h index 8c3d6d5..95aa79f 100644 --- a/herald/include/herald/zephyr_context.h +++ b/herald/include/herald/zephyr_context.h @@ -27,6 +27,7 @@ namespace herald { using namespace herald::ble; +/// \brief Internal zephyr namespace DO NOT USE - API MAY CHANGE WITHOUT WARNING namespace zephyrinternal { class Advertiser { public: @@ -42,9 +43,9 @@ namespace zephyrinternal { }; } -/* - * Zephyr context class - holds state generic across our application for a particular device. - */ +/// +/// \brief Holds generic state across our application for any Zephyr RTOS device. +/// class ZephyrContext : public Context, public BluetoothStateManager, public std::enable_shared_from_this { public: ZephyrContext(); @@ -77,6 +78,7 @@ class ZephyrContext : public Context, public BluetoothStateManager, public std:: // Other zephyr internal-but-public API base classes namespace zephyrinternal { +/// \brief INTERNAL utility class to allow Zephyr C API to call callbacks in the Zephyr internal Context Impl class. class Callbacks { public: Callbacks() = default; From 2ac6f2e3df73af3d0e1b38a3a0dbf0c0cc8f9adf Mon Sep 17 00:00:00 2001 From: Adam Fowler Date: Tue, 16 Mar 2021 08:36:42 +0000 Subject: [PATCH 05/28] Further analysis runner changes Known issues:- - Still doesn't compile due to std::pair not supporting moveable only types - Doesn't maintain references to analysis classes yet - Doesn't execute any analysis runs yet Signed-off-by: Adam Fowler --- herald-tests/analysisrunner-tests.cpp | 24 ++-- .../herald/analysis/distance_conversion.h | 2 +- herald/include/herald/analysis/runner.h | 105 ++++++------------ 3 files changed, 55 insertions(+), 76 deletions(-) diff --git a/herald-tests/analysisrunner-tests.cpp b/herald-tests/analysisrunner-tests.cpp index 3cf59da..ace2d25 100644 --- a/herald-tests/analysisrunner-tests.cpp +++ b/herald-tests/analysisrunner-tests.cpp @@ -24,7 +24,7 @@ struct DummyRSSISource { for (auto& v: data) { // devList.push(v.taken,v.value); // copy data over (It's unusual taking a SampleList and sending to a SampleList) if (v.taken.secondsSinceUnixEpoch() <= timeTo) { - runner.newSample<20>(key,v); + runner.template newSample(key,v); } } runner.run(timeTo); @@ -35,9 +35,9 @@ struct DummyRSSISource { SampleList,Sz> data; }; -struct DummyDistanceDelegate { - DummyDistanceDelegate() = default; - ~DummyDistanceDelegate() = default; +struct DummyDistanceDelegate : public herald::analysis::AnalysisDelegate { + DummyDistanceDelegate() : AnalysisDelegate() {}; + ~DummyDistanceDelegate() {}; // Detected methods by AnalysisRunner void newSample(SampledID sampled, Sample sample) { @@ -45,6 +45,16 @@ struct DummyDistanceDelegate { distances.push(sample); } + template + void newSample(SampledID sampled, Sample sample) { + ; // general handler - specialisation above handles Distance samples + } + + template + void setDestination(RunnerT& runner) { + // TODO in real life you'd care about this + } + void reset() { distances.clear(); lastSampledID = 0; @@ -83,8 +93,8 @@ TEST_CASE("analysisrunner-basic", "[analysisrunner][basic]") { herald::analysis::AnalysisRunner runner; // just for Sample types, and their produced output (Sample) - DummyDistanceDelegate myDelegate; - runner.add(distanceAnalyser); // so it picks up new data items + std::shared_ptr myDelegate = std::make_shared(); + runner.addAnalyser(distanceAnalyser); // so it picks up new data items runner.add(myDelegate); // so we receive the relevant output // run at different times and ensure that it only actually runs three times (sample size == 3) @@ -94,7 +104,7 @@ TEST_CASE("analysisrunner-basic", "[analysisrunner][basic]") { src.run(80,runner); src.run(95,runner); - auto& samples = myDelegate.samples(); + auto& samples = myDelegate->samples(); REQUIRE(samples.size() == 3); // didn't reach 4x30 seconds, so no tenth sample REQUIRE(samples[0].taken.secondsSinceUnixEpoch() == 30); REQUIRE(samples[0].value != 0.0); diff --git a/herald/include/herald/analysis/distance_conversion.h b/herald/include/herald/analysis/distance_conversion.h index 7fe0f55..ecdf38a 100644 --- a/herald/include/herald/analysis/distance_conversion.h +++ b/herald/include/herald/analysis/distance_conversion.h @@ -60,7 +60,7 @@ struct FowlerBasicAnalyser { ~FowlerBasicAnalyser() = default; template - void analyse(Date timeNow, const SampleList,SrcSz>& src, analysis::AnalysisDelegate& dst) { + void analyse(Date timeNow, const SampleList,SrcSz>& src, std::shared_ptr& dst) { if (lastRan + interval > timeNow) return; // interval guard basic.reset(); diff --git a/herald/include/herald/analysis/runner.h b/herald/include/herald/analysis/runner.h index 126448b..3e82c59 100644 --- a/herald/include/herald/analysis/runner.h +++ b/herald/include/herald/analysis/runner.h @@ -15,21 +15,19 @@ namespace analysis { using namespace sampling; -// FWD Alias Declaration, not definition -template +/// \brief Base Interface definition for classes that receive newSample callbacks from AnalysisRunner struct AnalysisDelegate { - AnalysisDelegate(Destination& dst) : destination(dst) {} - ~AnalysisDelegate() = default; + AnalysisDelegate() = default; + virtual ~AnalysisDelegate() = default; + template void newSample(SampledID sampled, Sample sample); template void setDestination(RunnerT& runner); - - Destination& destination; }; -/// Manages a set of lists for a particular Sample Value Type +/// \brief Manages a set of lists for a particular Sample Value Type template struct ListManager { using value_type = ValT; @@ -37,8 +35,8 @@ struct ListManager { ListManager() = default; ~ListManager() = default; - auto& list(const SampledID sampled) { - return lists.emplace(sampled).first; + decltype(auto) list(const SampledID sampled) { + return lists.try_emplace(lists.end(),sampled,{}).first; } void remove(const SampledID listFor) { @@ -49,13 +47,13 @@ struct ListManager { std::map,Size>> lists; }; -/// A fixed size set that holds exactly one instance of the std::variant for each +/// \brief A fixed size set that holds exactly one instance of the std::variant for each /// of the specified ValTs value types. template struct VariantSet { static constexpr std::size_t Size = sizeof...(ValTs); - VariantSet() = default; + VariantSet() : variants({ValTs()...}) {}; // Instantiate each type instance in the array ~VariantSet() = default; /// CAN THROW std::bad_variant_access @@ -87,6 +85,11 @@ struct VariantSet { /// // this ValueSource will call runner.newSample(Sample sample) /// }; +/// \brief Manages all sample lists, sources, sinks, and analysis instances for all data generated within a system +/// +/// This class can be used 'live' against real sensors, or statically with reference data. +/// This is achieved by ensuring the run(Date) method takes in the Date for the time of evaluation rather +/// than using the current Date. template struct AnalysisRunner { // using valueTypes = (typename SourceTypes::value_type)...; @@ -100,7 +103,7 @@ struct AnalysisRunner { template void newSample(SampledID sampled, sampling::Sample sample) { // incoming sample. Pass to correct list - lists.get>().list(sampled).push(sample); + lists.template get>().list(sampled).second.push(sample); } /// Run the relevant analyses given the current time point @@ -109,17 +112,21 @@ struct AnalysisRunner { // TODO performance enhancement - 'dirty' sample lists only (ones with new data) for (auto& listManager : lists) { using ValT = listManager::value_type; - for (auth& delegate : notifiables) { - if constexpr (std::is_same_v) { // SHOULD BE RUNNERS NOT DELEGATES - delegate. - } + for (auto& delegate : notifiables) { + // if constexpr (std::is_same_v) { // SHOULD BE RUNNERS NOT DELEGATES + //delegate. + // } } } } - template // TODO restrict this to one of the SourceTypes... types - void add(Destination& delegate) { - notifiables.push_back(std::variant...>(delegate)); + void add(std::shared_ptr delegate) { + notifiables.push_back(delegate); + } + + template + void addAnalyser(AnalyserT& analyser) { + // TODO fill this out and call its analyse() function when required } // /// callback from analysis data source @@ -131,59 +138,21 @@ struct AnalysisRunner { private: // TODO make sizes a parameterised list derived from template parameters VariantSet...> lists; // exactly one per value type - std::vector...>> notifiables; // more than one per value type - std::vector< // runners + std::vector> notifiables; // more than one per value type + //std::vector< // runners - template - void registerRunner(FirstT first) { - first.setDestination(*this); - } + // template + // void registerRunner(FirstT first) { + // first.setDestination(*this); + // } - template - void registerRunner(FirstT first,SecondT second,RestT... rest) { - first.setDestination(*this); - registerRunner(second,rest...); - } + // template + // void registerRunner(FirstT first,SecondT second,RestT... rest) { + // first.setDestination(*this); + // registerRunner(second,rest...); + // } }; - - - - - - - - -// template -// class AnalysisRunner { -// public: -// static constexpr std::size_t Size = sizeof...(AnalyserT); - -// AnalysisRunner(AnalyserT... analyserList) : analysers() { -// analysers.push(std::move(analyserList...)); -// } -// ~AnalysisRunner() = default; - -// // Public methods here -// template -// void add(AnalysisDelegate delegate) { - -// } - -// void run() { - -// } - -// // template -// SampleList& list(sampling::SampledID sampled) { - -// } - -// private: -// std::array,Size> analysers; -// std::map< -// }; - } } From 5801e43e4b2bd111f34d0de63182c96aa96d2fc7 Mon Sep 17 00:00:00 2001 From: Adam Fowler Date: Tue, 16 Mar 2021 22:48:09 +0000 Subject: [PATCH 06/28] Added multi platform build helpers for Zephyr Various Zephyr build improvements:- - Added an ubuntu dependencies installation script for automation - Added a build all script for the 4 Nordic Semiconductor platforms we use (nRF52832DK, nRF52833DK, nRF52840DK, nRF5340DK) - Tested all zephyr configuration and main.cpp build for all platforms for both the wearable and venue beacon demo apps - Waiting to fix a Herald API bug before completing the hex file build script and build of distribution zip file Signed-off-by: Adam Fowler --- .gitignore | 3 +- config/zephyr/nRF52832.conf | 2 +- config/zephyr/nRF52833.conf | 18 +++++ config/zephyr/nRF52840.conf | 2 +- herald-venue-beacon/CMakeLists.txt | 6 ++ herald-wearable/CMakeLists.txt | 6 ++ herald-wearable/src/main.cpp | 12 ++- herald/include/herald/analysis/runner.h | 4 +- install-deps-ubuntu.sh | 51 ++++++++++++ make-all-zephyr.bat | 38 +++++++++ make-all-zephyr.sh | 100 ++++++++++++++++++++++++ 11 files changed, 236 insertions(+), 6 deletions(-) create mode 100644 config/zephyr/nRF52833.conf create mode 100644 install-deps-ubuntu.sh create mode 100644 make-all-zephyr.bat create mode 100644 make-all-zephyr.sh diff --git a/.gitignore b/.gitignore index 48ab5f0..7f4641d 100644 --- a/.gitignore +++ b/.gitignore @@ -4,4 +4,5 @@ Output *.exe *.so *.a -**/.DS_Store \ No newline at end of file +**/.DS_Store +**/build-* \ No newline at end of file diff --git a/config/zephyr/nRF52832.conf b/config/zephyr/nRF52832.conf index eccc954..3d2447b 100644 --- a/config/zephyr/nRF52832.conf +++ b/config/zephyr/nRF52832.conf @@ -14,5 +14,5 @@ CONFIG_MBEDTLS_CFG_FILE="config-tls-generic.h" # CONFIG_TINYCRYPT_SHA256=y # Simple Payload Support ENDS -CONFIG_BT_MAX_CONN=128 +CONFIG_BT_MAX_CONN=20 # VERIFY above for this machine \ No newline at end of file diff --git a/config/zephyr/nRF52833.conf b/config/zephyr/nRF52833.conf new file mode 100644 index 0000000..3d2447b --- /dev/null +++ b/config/zephyr/nRF52833.conf @@ -0,0 +1,18 @@ + +CONFIG_NRF_SECURITY_RNG=y +CONFIG_ENTROPY_GENERATOR=y + +# Simple Payload support BEGINS +# Option 1: MBEDTLS +CONFIG_MBEDTLS_VANILLA_BACKEND=y +CONFIG_MBEDTLS_MAC_SHA256_ENABLED=y +CONFIG_MBEDTLS=y +CONFIG_MBEDTLS_BUILTIN=y +CONFIG_MBEDTLS_CFG_FILE="config-tls-generic.h" + +# Option 2: TINYCRYPT +# CONFIG_TINYCRYPT_SHA256=y +# Simple Payload Support ENDS + +CONFIG_BT_MAX_CONN=20 +# VERIFY above for this machine \ No newline at end of file diff --git a/config/zephyr/nRF52840.conf b/config/zephyr/nRF52840.conf index e7d5e90..1787671 100644 --- a/config/zephyr/nRF52840.conf +++ b/config/zephyr/nRF52840.conf @@ -19,5 +19,5 @@ CONFIG_MBEDTLS_CFG_FILE="config-tls-generic.h" # CONFIG_TINYCRYPT_SHA256=y # Simple Payload Support ENDS -CONFIG_BT_MAX_CONN=128 +CONFIG_BT_MAX_CONN=20 # VERIFY above for this machine \ No newline at end of file diff --git a/herald-venue-beacon/CMakeLists.txt b/herald-venue-beacon/CMakeLists.txt index 00ec4d0..674ef4d 100644 --- a/herald-venue-beacon/CMakeLists.txt +++ b/herald-venue-beacon/CMakeLists.txt @@ -10,6 +10,7 @@ set(NO_BUILD_TYPE_WARNING ON) # Zephyr build environment variables set here #set(ENV{BOARD} nrf52840dongle_nrf52840) # nRF52840 USB dongle #set(ENV{BOARD} nrf52dk_nrf52832) # nRF52832 USB dongle +#set(ENV{BOARD} nrf52833dk_nrf52833) # nRF52833 DK #set(ENV{BOARD} nrf5340dk_nrf5340_cpuapp) # nRF5340 DK using secure app core # Detect if already set. If not, default to latest nRF DK board (5340) if(DEFINED ENV{BOARD}) @@ -44,6 +45,11 @@ elseif($ENV{BOARD} MATCHES .*nrf52832.*) ${OVERLAY_CONFIG} ${BASE_CONFIG}/nrf52832.conf ) +elseif($ENV{BOARD} MATCHES .*nrf52833.*) + set(OVERLAY_CONFIG + ${OVERLAY_CONFIG} + ${BASE_CONFIG}/nrf52833.conf + ) endif() if(CMAKE_BUILD_TYPE MATCHES Debug) set(OVERLAY_CONFIG diff --git a/herald-wearable/CMakeLists.txt b/herald-wearable/CMakeLists.txt index 469338b..912ffff 100644 --- a/herald-wearable/CMakeLists.txt +++ b/herald-wearable/CMakeLists.txt @@ -12,6 +12,7 @@ set(NO_BUILD_TYPE_WARNING ON) # Zephyr build environment variables set here #set(ENV{BOARD} nrf52840dongle_nrf52840) # nRF52840 USB dongle #set(ENV{BOARD} nrf52dk_nrf52832) # nRF52832 USB dongle +#set(ENV{BOARD} nrf52833dk_nrf52833) # nRF52833 DK #set(ENV{BOARD} nrf5340dk_nrf5340_cpuapp) # nRF5340 DK using secure app core # Detect if already set. If not, default to latest nRF DK board (5340) if(DEFINED ENV{BOARD}) @@ -46,6 +47,11 @@ elseif($ENV{BOARD} MATCHES .*nrf52832.*) ${OVERLAY_CONFIG} ${BASE_CONFIG}/nrf52832.conf ) +elseif($ENV{BOARD} MATCHES .*nrf52833.*) + set(OVERLAY_CONFIG + ${OVERLAY_CONFIG} + ${BASE_CONFIG}/nrf52833.conf + ) endif() if(CMAKE_BUILD_TYPE MATCHES Debug) set(OVERLAY_CONFIG diff --git a/herald-wearable/src/main.cpp b/herald-wearable/src/main.cpp index 0c87e4d..5ae3863 100644 --- a/herald-wearable/src/main.cpp +++ b/herald-wearable/src/main.cpp @@ -26,10 +26,11 @@ #include #include - +#ifdef CC3XX_BACKEND // Cryptocell - nRF52840/nRF9160/nRF53x only. See prj.conf too to enable this Hardware #include #include +#endif #include @@ -126,6 +127,7 @@ class AppLoggingDelegate : public herald::SensorDelegate { } }; +#ifdef CC3XX_BACKEND void cc3xx_init() { // START IMPLEMENTORS GUIDANCE - EXAMPLE CODE NOT NEEDED TO COPY IN TO IN YOUR DEMO APP // NOTE TO IMPLEMENTORS: Please remember to use a hardware security module where present. @@ -159,6 +161,7 @@ void cc3xx_init() { } // END IMPLEMENTORS GUIDANCE } +#endif void herald_entry() { LOG_DBG("Herald entry"); @@ -219,7 +222,12 @@ void herald_entry() { size_t buflen = 2048; uint8_t* buf = new uint8_t[buflen]; size_t olen = 0; + +#ifdef CC3XX_BACKEND int success = nrf_cc3xx_platform_entropy_get(buf,buflen,&olen); +#else + int success = 1; +#endif if (0 == success) { sk.clear(); sk.append(buf, 0, buflen); @@ -336,7 +344,9 @@ void main(void) LOG_DBG("Const char* param test: %s","some string param"); LOG_DBG("int param test: %d",1234); +#ifdef CC3XX_BACKEND cc3xx_init(); +#endif // Start herald entry on a new thread in case of errors, or needing to do something on the main thread k_tid_t herald_pid = k_thread_create(&herald_thread, herald_stack, 4096, diff --git a/herald/include/herald/analysis/runner.h b/herald/include/herald/analysis/runner.h index 3e82c59..26c4afc 100644 --- a/herald/include/herald/analysis/runner.h +++ b/herald/include/herald/analysis/runner.h @@ -137,8 +137,8 @@ struct AnalysisRunner { private: // TODO make sizes a parameterised list derived from template parameters - VariantSet...> lists; // exactly one per value type - std::vector> notifiables; // more than one per value type + VariantSet...> lists; // exactly one list manager per value type + std::vector> notifiables; // more than one delegate per value type //std::vector< // runners // template diff --git a/install-deps-ubuntu.sh b/install-deps-ubuntu.sh new file mode 100644 index 0000000..7982878 --- /dev/null +++ b/install-deps-ubuntu.sh @@ -0,0 +1,51 @@ +#!/bin/sh + +# Run this ONCE +# Then run the make-all-zephyr.sh file + +ORIG=$PWD + +sudo apt update +sudo apt upgrade + +# Note: Be sure to set ZEPHYR_BASE before calling this file: E.g. D:\devtools\ncs\v1.5.0\zephyr +sudo apt install --no-install-recommends git cmake ninja-build gperf \ +ccache dfu-util device-tree-compiler wget \ +python3-dev python3-pip python3-setuptools python3-tk python3-wheel xz-utils file \ +make gcc gcc-multilib g++-multilib libsdl2-dev + +mkdir ~/gn && cd ~/gn +wget -O gn.zip https://chrome-infra-packages.appspot.com/dl/gn/gn/linux-amd64/+/latest +unzip gn.zip +rm gn.zip +echo 'export PATH=${HOME}/gn:"$PATH"' >> ~/.bashrc +source ~/.bashrc + +cd ~/ +wget https://developer.arm.com/-/media/Files/downloads/gnu-rm/10-2020q4/gcc-arm-none-eabi-10-2020-q4-major-x86_64-linux.tar.bz +mkdir gnuarmemb +bunzip2 gcc-arm-none-eabi-10-2020-q4-major-x86_64-linux.tar.bz +cd gnuarmemb/ +tar xf gcc-arm-none-eabi-10-2020-q4-major-x86_64-linux.tar +export ZEPHYR_TOOLCHAIN_VARIANT=gnuarmemb +export GNUARMEMB_TOOLCHAIN_PATH="~/gnuarmemb/gcc-arm-none-eabi-10-2020-q4-major" + + +# Now install Nordic connect - https://developer.nordicsemi.com/nRF_Connect_SDK/doc/latest/nrf/gs_installing.html +mkdir ~/ncs +cd ~/ncs +west init -m https://github.com/nrfconnect/sdk-nrf --mr v1.5.0 +west update +west zephyr-export + +source ~/ncs/zephyr/zephyr-env.sh +cd ~/ncs +pip3 install --user -r zephyr/scripts/requirements.txt +pip3 install --user -r nrf/scripts/requirements.txt +pip3 install --user -r bootloader/mcuboot/scripts/requirements.txt + +cd $ORIG + +echo "Completed Zephyr dependency installation" + +exit 0 diff --git a/make-all-zephyr.bat b/make-all-zephyr.bat new file mode 100644 index 0000000..678d9b3 --- /dev/null +++ b/make-all-zephyr.bat @@ -0,0 +1,38 @@ + +REM WORK IN PROGRESS - DO NOT USE YET - See Ubuntu sh version for inspiration + +cd herald-wearable +mkdir build-5340 +mkdir build-52832 +mkdir build-52840 +mkdir build-52833 + +set ARCH=arm +set CXX=C:\Program Files (x86)\GNU Arm Embedded Toolchain\10 2020-q4-major\bin\arm-none-eabi-g++.exe +set CC=C:\Program Files (x86)\GNU Arm Embedded Toolchain\10 2020-q4-major\bin\arm-none-eabi-gcc.exe + +cd build-5340 +set BOARD=nrf5340dk_nrf5340_cpuapp +cmake .. +make +cd .. + +cd build-52840 +set BOARD=nrf52840dongle_nrf52840 +cmake .. +make +cd .. + +cd build-52832 +set BOARD=nrf52dk_nrf52832 +cmake .. +make +cd .. + +cd build-52833 +set BOARD=nrf52833dk_nrf52833 +cmake .. +make +cd .. + +echo "Done" diff --git a/make-all-zephyr.sh b/make-all-zephyr.sh new file mode 100644 index 0000000..81c6733 --- /dev/null +++ b/make-all-zephyr.sh @@ -0,0 +1,100 @@ +#!/bin/sh + +ORIG=$PWD + +source ~/ncs/zephyr/zephyr-env.sh +export ARCH=arm + +cd herald-wearable + +mkdir build-5340 +mkdir build-52832 +mkdir build-52840 +mkdir build-52833 + +cd build-5340 +export BOARD=nrf5340dk_nrf5340_cpuapp +cmake .. +make -j 32 +cd .. + +cd build-52840 +export BOARD=nrf52840dongle_nrf52840 +cmake .. +make -j 32 +cd .. + +cd build-52832 +export BOARD=nrf52dk_nrf52832 +cmake .. +make -j 32 +cd .. + +cd build-52833 +export BOARD=nrf52833dk_nrf52833 +cmake .. +make -j 32 +cd .. + +cd .. +cd herald-venue-beacon + +mkdir build-5340 +mkdir build-52832 +mkdir build-52840 +mkdir build-52833 + +cd build-5340 +export BOARD=nrf5340dk_nrf5340_cpuapp +cmake .. +make -j 32 +cd .. + +cd build-52840 +export BOARD=nrf52840dongle_nrf52840 +cmake .. +make -j 32 +cd .. + +cd build-52832 +export BOARD=nrf52dk_nrf52832 +cmake .. +make -j 32 +cd .. + +cd build-52833 +export BOARD=nrf52833dk_nrf52833 +cmake .. +make -j 32 +cd .. + +cd .. + +mkdir build-zephyr +cd build-zephyr +mkdir nrf5340dk +mkdir nrf52832dk +mkdir nrf52833dk +mkdir nrf52840dk +mkdir nrf5340dk/herald-wearable +mkdir nrf52832dk/herald-wearable +mkdir nrf52833dk/herald-wearable +mkdir nrf52840dk/herald-wearable +mkdir nrf5340dk/herald-venue-beacon +mkdir nrf52832dk/herald-venue-beacon +mkdir nrf52833dk/herald-venue-beacon +mkdir nrf52840dk/herald-venue-beacon +cp ../herald-wearable/build-5340/zephyr/merged.hex nrf5340dk/herald-wearable/ +cp ../herald-wearable/build-5340/hci_rpmsg/zephyr/zephyr.hex nrf5340dk/herald-wearable/ +cp ../herald-wearable/build-52840/zephyr/zephyr.hex nrf52840dk/herald-wearable/ +cp ../herald-wearable/build-52832/zephyr/zephyr.hex nrf52832dk/herald-wearable/ +cp ../herald-wearable/build-52833/zephyr/zephyr.hex nrf52833dk/herald-wearable/ +cp ../herald-venue-beacon/build-5340/zephyr/merged.hex nrf5340dk/herald-venue-beacon/ +cp ../herald-venue-beacon/build-5340/hci_rpmsg/zephyr/zephyr.hex nrf5340dk/herald-venue-beacon/ +cp ../herald-venue-beacon/build-52840/zephyr/zephyr.hex nrf52840dk/herald-venue-beacon/ +cp ../herald-venue-beacon/build-52832/zephyr/zephyr.hex nrf52832dk/herald-venue-beacon/ +cp ../herald-venue-beacon/build-52833/zephyr/zephyr.hex nrf52833dk/herald-venue-beacon/ +tar cjf zephyr-binaries.tar.gz nrf* +cd .. + +echo "Done" From f2d5d3905b86e5866c10757cc650dd5dd7b5c530 Mon Sep 17 00:00:00 2001 From: Adam Fowler Date: Wed, 17 Mar 2021 09:14:55 +0000 Subject: [PATCH 07/28] VariantSet and ListManager working reliably No copy construction, one instance per variant type Signed-off-by: Adam Fowler --- herald-tests/analysisrunner-tests.cpp | 249 ++++++++++++++-------- herald/include/herald/analysis/runner.h | 213 ++++++++++++------ herald/include/herald/analysis/sampling.h | 2 +- 3 files changed, 302 insertions(+), 162 deletions(-) diff --git a/herald-tests/analysisrunner-tests.cpp b/herald-tests/analysisrunner-tests.cpp index ace2d25..235c466 100644 --- a/herald-tests/analysisrunner-tests.cpp +++ b/herald-tests/analysisrunner-tests.cpp @@ -9,70 +9,135 @@ using namespace herald::analysis::sampling; using namespace herald::datatype; -template -struct DummyRSSISource { - using value_type = Sample; // allows AnalysisRunner to introspect this class at compile time - - DummyRSSISource(const std::size_t srcDeviceKey, SampleList,Sz>&& data) : key(srcDeviceKey), data(std::move(data)) {}; - ~DummyRSSISource() = default; - - template - void run(int timeTo, RunnerT& runner) { - // get reference to target list - // auto& devList = runner.list(key); - // push through data at default rate - for (auto& v: data) { - // devList.push(v.taken,v.value); // copy data over (It's unusual taking a SampleList and sending to a SampleList) - if (v.taken.secondsSinceUnixEpoch() <= timeTo) { - runner.template newSample(key,v); - } - } - runner.run(timeTo); +// template +// struct DummyRSSISource { +// using value_type = Sample; // allows AnalysisRunner to introspect this class at compile time + +// DummyRSSISource(const std::size_t srcDeviceKey, SampleList,Sz>&& data) : key(srcDeviceKey), data(std::move(data)) {}; +// ~DummyRSSISource() = default; + +// template +// void run(int timeTo, RunnerT& runner) { +// // get reference to target list +// // auto& devList = runner.list(key); +// // push through data at default rate +// for (auto& v: data) { +// // devList.push(v.taken,v.value); // copy data over (It's unusual taking a SampleList and sending to a SampleList) +// if (v.taken.secondsSinceUnixEpoch() <= timeTo) { +// runner.template newSample(key,v); +// } +// } +// runner.run(timeTo); +// } + +// private: +// std::size_t key; +// SampleList,Sz> data; +// }; + +// struct DummyDistanceDelegate : public herald::analysis::AnalysisDelegate { +// DummyDistanceDelegate() : AnalysisDelegate() {}; +// ~DummyDistanceDelegate() {}; + +// // Detected methods by AnalysisRunner +// void newSample(SampledID sampled, Sample sample) { +// lastSampledID = sampled; +// distances.push(sample); +// } + +// template +// void newSample(SampledID sampled, Sample sample) { +// ; // general handler - specialisation above handles Distance samples +// } + +// template +// void setDestination(RunnerT& runner) { +// // TODO in real life you'd care about this +// } + +// void reset() { +// distances.clear(); +// lastSampledID = 0; +// } + +// // Test only methods +// SampledID lastSampled() { +// return lastSampledID; +// } + +// const SampleList,20>& samples() { +// return distances; +// } + +// private: +// SampledID lastSampledID; +// SampleList,20> distances; +// }; + + +TEST_CASE("variantset-basic", "[variantset][basic]") { + SECTION("variantset-basic") { + herald::analysis::VariantSet vs; + REQUIRE(vs.size() == 2); + int intValue = vs.get(); + REQUIRE(intValue == 0); + double dblValue = vs.get(); + REQUIRE(dblValue == 0.0); } +} + +TEST_CASE("variantset-lists", "[variantset][lists]") { + SECTION("variantset-lists") { + herald::analysis::VariantSet,15>,SampleList,15>> vs; + REQUIRE(vs.size() == 2); + SampleList,15>& intValue = vs.get,15>>(); + REQUIRE(intValue.size() == 0); + SampleList,15>& dblValue = vs.get,15>>(); + REQUIRE(dblValue.size() == 0); + + intValue.push(0,12); + dblValue.push(10,14.3); + dblValue.push(20,15.3); + + SampleList,15>& intValue2 = vs.get,15>>(); + REQUIRE(intValue2.size() == 1); + SampleList,15>& dblValue2 = vs.get,15>>(); + REQUIRE(dblValue2.size() == 2); -private: - std::size_t key; - SampleList,Sz> data; -}; - -struct DummyDistanceDelegate : public herald::analysis::AnalysisDelegate { - DummyDistanceDelegate() : AnalysisDelegate() {}; - ~DummyDistanceDelegate() {}; - - // Detected methods by AnalysisRunner - void newSample(SampledID sampled, Sample sample) { - lastSampledID = sampled; - distances.push(sample); - } - - template - void newSample(SampledID sampled, Sample sample) { - ; // general handler - specialisation above handles Distance samples - } - - template - void setDestination(RunnerT& runner) { - // TODO in real life you'd care about this } +} + +TEST_CASE("variantset-listmanager", "[variantset][listmanager]") { + SECTION("variantset-listmanager") { + using herald::analysis::ListManager; + herald::analysis::VariantSet,ListManager> vs; + REQUIRE(vs.size() == 2); + ListManager& intValue = vs.template get>(); + REQUIRE(intValue.size() == 0); + ListManager& dblValue = vs.template get>(); + REQUIRE(dblValue.size() == 0); + + SampleList,15>& intList = intValue.list(1234); // list for entity being sampled with SampledId=1234 + REQUIRE(intList.size() == 0); + SampleList,15>& dblList = dblValue.list(5678); // list for entity being sampled with SampledId=1234 + REQUIRE(dblList.size() == 0); + + intList.push(Sample(0,12)); + dblList.push(Sample(10,14.3)); + dblList.push(Sample(20,15.3)); + + ListManager& intValue2 = vs.template get>(); + REQUIRE(intValue2.size() == 1); + ListManager& dblValue2 = vs.template get>(); + REQUIRE(dblValue2.size() == 1); + + SampleList,15>& intList2 = intValue2.list(1234); // list for entity being sampled with SampledId=1234 + REQUIRE(intList2.size() == 1); + SampleList,15>& dblList2 = dblValue2.list(5678); // list for entity being sampled with SampledId=1234 + REQUIRE(dblList2.size() == 2); - void reset() { - distances.clear(); - lastSampledID = 0; } - - // Test only methods - SampledID lastSampled() { - return lastSampledID; - } - - const SampleList,20>& samples() { - return distances; - } - -private: - SampledID lastSampledID; - SampleList,20> distances; -}; +} /// [Who] As a DCT app developer /// [What] I want to link my live application data to an analysis runner easily @@ -81,36 +146,36 @@ struct DummyDistanceDelegate : public herald::analysis::AnalysisDelegate { /// [Who] As a DCT app developer /// [What] I want to periodically run analysis aggregates automatically /// [Value] So I don't miss any information, and have accurate, regular, samples -TEST_CASE("analysisrunner-basic", "[analysisrunner][basic]") { - SECTION("analysisrunner-basic") { - SampleList,10> srcData{ - Sample{10,-55},Sample{20,-55},Sample{30,-55},Sample{40,-55},Sample{50,-55}, - Sample{60,-55},Sample{70,-55},Sample{80,-55},Sample{90,-55},Sample{100,-55} - }; - DummyRSSISource src(1234,std::move(srcData)); - - herald::analysis::algorithms::distance::FowlerBasicAnalyser distanceAnalyser(30, -50, -24); - - herald::analysis::AnalysisRunner runner; // just for Sample types, and their produced output (Sample) - - std::shared_ptr myDelegate = std::make_shared(); - runner.addAnalyser(distanceAnalyser); // so it picks up new data items - runner.add(myDelegate); // so we receive the relevant output - - // run at different times and ensure that it only actually runs three times (sample size == 3) - src.run(20,runner); - src.run(40,runner); - src.run(60,runner); - src.run(80,runner); - src.run(95,runner); - - auto& samples = myDelegate->samples(); - REQUIRE(samples.size() == 3); // didn't reach 4x30 seconds, so no tenth sample - REQUIRE(samples[0].taken.secondsSinceUnixEpoch() == 30); - REQUIRE(samples[0].value != 0.0); - REQUIRE(samples[1].taken.secondsSinceUnixEpoch() == 60); - REQUIRE(samples[1].value != 0.0); - REQUIRE(samples[2].taken.secondsSinceUnixEpoch() == 90); - REQUIRE(samples[2].value != 0.0); - } -} \ No newline at end of file +// TEST_CASE("analysisrunner-basic", "[analysisrunner][basic]") { +// SECTION("analysisrunner-basic") { +// SampleList,10> srcData{ +// Sample{10,-55},Sample{20,-55},Sample{30,-55},Sample{40,-55},Sample{50,-55}, +// Sample{60,-55},Sample{70,-55},Sample{80,-55},Sample{90,-55},Sample{100,-55} +// }; +// DummyRSSISource src(1234,std::move(srcData)); + +// herald::analysis::algorithms::distance::FowlerBasicAnalyser distanceAnalyser(30, -50, -24); + +// herald::analysis::AnalysisRunner runner; // just for Sample types, and their produced output (Sample) + +// std::shared_ptr myDelegate = std::make_shared(); +// runner.addAnalyser(distanceAnalyser); // so it picks up new data items +// runner.add(myDelegate); // so we receive the relevant output + +// // run at different times and ensure that it only actually runs three times (sample size == 3) +// src.run(20,runner); +// src.run(40,runner); +// src.run(60,runner); +// src.run(80,runner); +// src.run(95,runner); + +// auto& samples = myDelegate->samples(); +// REQUIRE(samples.size() == 3); // didn't reach 4x30 seconds, so no tenth sample +// REQUIRE(samples[0].taken.secondsSinceUnixEpoch() == 30); +// REQUIRE(samples[0].value != 0.0); +// REQUIRE(samples[1].taken.secondsSinceUnixEpoch() == 60); +// REQUIRE(samples[1].value != 0.0); +// REQUIRE(samples[2].taken.secondsSinceUnixEpoch() == 90); +// REQUIRE(samples[2].value != 0.0); +// } +// } \ No newline at end of file diff --git a/herald/include/herald/analysis/runner.h b/herald/include/herald/analysis/runner.h index 26c4afc..e8dcdff 100644 --- a/herald/include/herald/analysis/runner.h +++ b/herald/include/herald/analysis/runner.h @@ -28,24 +28,83 @@ struct AnalysisDelegate { }; /// \brief Manages a set of lists for a particular Sample Value Type -template +template struct ListManager { using value_type = ValT; - ListManager() = default; + ListManager() noexcept : lists(), nextPos(0) {}; + ListManager(ListManager&& other) : lists(std::move(other.lists)), nextPos(other.nextPos) {}; // move ctor ~ListManager() = default; - decltype(auto) list(const SampledID sampled) { - return lists.try_emplace(lists.end(),sampled,{}).first; + SampleList,Size>& list(const SampledID sampled) { + for (auto& entry : lists) { + if (entry.key() == sampled) { + return entry.value(); + } + } + auto& val = lists[nextPos++]; + return val.emplace(sampled); } void remove(const SampledID listFor) { - lists.erase(listFor); + // lists.erase(listFor); + // TODO delete value and re-use its allocation space + } + + const std::size_t size() const { + return nextPos; } private: - std::map,Size>> lists; + template + struct ListManagerEntry { + ListManagerEntry() : mKey(), mValue() {} + ~ListManagerEntry() = default; + + SampleList,Size>& emplace(const Key k) { + mKey = k; + return value(); + } + + const Key key() const { + return mKey; + } + + SampleList,Size>& value() { + return mValue; + } + + private: + Key mKey; + SampleList,Size> mValue; + }; + + std::array,MaxLists> lists; + int nextPos; }; +// template +// struct ListManager { +// using value_type = ValT; + +// ListManager() = default; +// ~ListManager() = default; + +// SampleList,Size>& list(const SampledID sampled) { +// auto iter = lists.try_emplace(sampled).first; +// return lists.at(sampled); +// } + +// void remove(const SampledID listFor) { +// lists.erase(listFor); +// } + +// const std::size_t size() const { +// return lists.size(); +// } + +// private: +// std::map,Size>> lists; +// }; /// \brief A fixed size set that holds exactly one instance of the std::variant for each /// of the specified ValTs value types. @@ -53,7 +112,9 @@ template struct VariantSet { static constexpr std::size_t Size = sizeof...(ValTs); - VariantSet() : variants({ValTs()...}) {}; // Instantiate each type instance in the array + VariantSet() : variants() { + createInstances(0); + }; // Instantiate each type instance in the array ~VariantSet() = default; /// CAN THROW std::bad_variant_access @@ -67,8 +128,22 @@ struct VariantSet { throw std::bad_variant_access(); } + const std::size_t size() const { + return variants.size(); + } + private: std::array,Size> variants; + template + void createInstances(int pos) { + variants[pos].template emplace(); + } + + template + void createInstances(int pos) { + variants[pos].template emplace(); + createInstances(pos + 1); + } }; /// The below is an example ValueSource... @@ -90,68 +165,68 @@ struct VariantSet { /// This class can be used 'live' against real sensors, or statically with reference data. /// This is achieved by ensuring the run(Date) method takes in the Date for the time of evaluation rather /// than using the current Date. -template -struct AnalysisRunner { - // using valueTypes = (typename SourceTypes::value_type)...; - - AnalysisRunner(/*SourceTypes&... sources*/) : lists(), notifiables() { - // registerRunner(sources...); - } - ~AnalysisRunner() = default; - - /// We are an analysis delegate ourselves - this is used by Source types, and by producers (analysis runners) - template - void newSample(SampledID sampled, sampling::Sample sample) { - // incoming sample. Pass to correct list - lists.template get>().list(sampled).second.push(sample); - } - - /// Run the relevant analyses given the current time point - void run(Date timeNow) { - // call analyse(dateNow,srcList,dstDelegate) for all delegates with the correct list each, for each sampled - // TODO performance enhancement - 'dirty' sample lists only (ones with new data) - for (auto& listManager : lists) { - using ValT = listManager::value_type; - for (auto& delegate : notifiables) { - // if constexpr (std::is_same_v) { // SHOULD BE RUNNERS NOT DELEGATES - //delegate. - // } - } - } - } - - void add(std::shared_ptr delegate) { - notifiables.push_back(delegate); - } - - template - void addAnalyser(AnalyserT& analyser) { - // TODO fill this out and call its analyse() function when required - } - - // /// callback from analysis data source - // template - // SampleList>& list(const SampledID sampled) { - // return lists.get().list(sampled); - // } - -private: - // TODO make sizes a parameterised list derived from template parameters - VariantSet...> lists; // exactly one list manager per value type - std::vector> notifiables; // more than one delegate per value type - //std::vector< // runners - - // template - // void registerRunner(FirstT first) { - // first.setDestination(*this); - // } - - // template - // void registerRunner(FirstT first,SecondT second,RestT... rest) { - // first.setDestination(*this); - // registerRunner(second,rest...); - // } -}; +// template +// struct AnalysisRunner { +// // using valueTypes = (typename SourceTypes::value_type)...; + +// AnalysisRunner(/*SourceTypes&... sources*/) : lists(), notifiables() { +// // registerRunner(sources...); +// } +// ~AnalysisRunner() = default; + +// /// We are an analysis delegate ourselves - this is used by Source types, and by producers (analysis runners) +// template +// void newSample(SampledID sampled, sampling::Sample sample) { +// // incoming sample. Pass to correct list +// lists.template get>().list(sampled)->second.push(sample); +// } + +// /// Run the relevant analyses given the current time point +// void run(Date timeNow) { +// // call analyse(dateNow,srcList,dstDelegate) for all delegates with the correct list each, for each sampled +// // TODO performance enhancement - 'dirty' sample lists only (ones with new data) +// for (auto& listManager : lists) { +// using ValT = listManager::value_type; +// for (auto& delegate : notifiables) { +// // if constexpr (std::is_same_v) { // SHOULD BE RUNNERS NOT DELEGATES +// //delegate. +// // } +// } +// } +// } + +// void add(std::shared_ptr delegate) { +// notifiables.push_back(delegate); +// } + +// template +// void addAnalyser(AnalyserT& analyser) { +// // TODO fill this out and call its analyse() function when required +// } + +// // /// callback from analysis data source +// // template +// // SampleList>& list(const SampledID sampled) { +// // return lists.get().list(sampled); +// // } + +// private: +// // TODO make sizes a parameterised list derived from template parameters +// VariantSet...> lists; // exactly one list manager per value type +// std::vector> notifiables; // more than one delegate per value type +// //std::vector< // runners + +// // template +// // void registerRunner(FirstT first) { +// // first.setDestination(*this); +// // } + +// // template +// // void registerRunner(FirstT first,SecondT second,RestT... rest) { +// // first.setDestination(*this); +// // registerRunner(second,rest...); +// // } +// }; } } diff --git a/herald/include/herald/analysis/sampling.h b/herald/include/herald/analysis/sampling.h index d1d63c2..218d7f9 100644 --- a/herald/include/herald/analysis/sampling.h +++ b/herald/include/herald/analysis/sampling.h @@ -109,7 +109,7 @@ struct SampleList { SampleList() : data(), oldestPosition(SIZE_MAX), newestPosition(SIZE_MAX) {} SampleList(const SampleList&) = delete; // no shallow copies allowed - SampleList(SampleList&& other) : data(std::move(other.data)), oldestPosition(other.oldestPosition), newestPosition(other.newestPosition) {} // move ctor + SampleList(SampleList&& other) noexcept : data(std::move(other.data)), oldestPosition(other.oldestPosition), newestPosition(other.newestPosition) {} // move ctor // Creates a list from static initialiser list elements, using deduction guide template From 17961ef2ca4258208779f1f35102acd71f586e9a Mon Sep 17 00:00:00 2001 From: Adam Fowler Date: Sat, 20 Mar 2021 14:52:07 +0000 Subject: [PATCH 08/28] Part of #42. Analysis Runner working with tests. Signed-off-by: Adam Fowler --- herald-tests/analysisrunner-tests.cpp | 216 ++++++----- .../herald/analysis/distance_conversion.h | 40 +- herald/include/herald/analysis/ranges.h | 8 + herald/include/herald/analysis/runner.h | 352 ++++++++++-------- herald/include/herald/analysis/sampling.h | 14 + 5 files changed, 372 insertions(+), 258 deletions(-) diff --git a/herald-tests/analysisrunner-tests.cpp b/herald-tests/analysisrunner-tests.cpp index 235c466..a02ee0b 100644 --- a/herald-tests/analysisrunner-tests.cpp +++ b/herald-tests/analysisrunner-tests.cpp @@ -6,73 +6,73 @@ #include "herald/herald.h" +#include + using namespace herald::analysis::sampling; using namespace herald::datatype; -// template -// struct DummyRSSISource { -// using value_type = Sample; // allows AnalysisRunner to introspect this class at compile time - -// DummyRSSISource(const std::size_t srcDeviceKey, SampleList,Sz>&& data) : key(srcDeviceKey), data(std::move(data)) {}; -// ~DummyRSSISource() = default; - -// template -// void run(int timeTo, RunnerT& runner) { -// // get reference to target list -// // auto& devList = runner.list(key); -// // push through data at default rate -// for (auto& v: data) { -// // devList.push(v.taken,v.value); // copy data over (It's unusual taking a SampleList and sending to a SampleList) -// if (v.taken.secondsSinceUnixEpoch() <= timeTo) { -// runner.template newSample(key,v); -// } -// } -// runner.run(timeTo); -// } - -// private: -// std::size_t key; -// SampleList,Sz> data; -// }; - -// struct DummyDistanceDelegate : public herald::analysis::AnalysisDelegate { -// DummyDistanceDelegate() : AnalysisDelegate() {}; -// ~DummyDistanceDelegate() {}; - -// // Detected methods by AnalysisRunner -// void newSample(SampledID sampled, Sample sample) { -// lastSampledID = sampled; -// distances.push(sample); -// } - -// template -// void newSample(SampledID sampled, Sample sample) { -// ; // general handler - specialisation above handles Distance samples -// } - -// template -// void setDestination(RunnerT& runner) { -// // TODO in real life you'd care about this -// } - -// void reset() { -// distances.clear(); -// lastSampledID = 0; -// } - -// // Test only methods -// SampledID lastSampled() { -// return lastSampledID; -// } - -// const SampleList,20>& samples() { -// return distances; -// } - -// private: -// SampledID lastSampledID; -// SampleList,20> distances; -// }; +template +struct DummyRSSISource { + using value_type = Sample; // allows AnalysisRunner to introspect this class at compile time + + DummyRSSISource(const std::size_t srcDeviceKey, SampleList,Sz>&& data) : key(srcDeviceKey), data(std::move(data)) {}; + ~DummyRSSISource() = default; + + template + void run(int timeTo, RunnerT& runner) { + // push through data at default rate + for (auto& v: data) { + // devList.push(v.taken,v.value); // copy data over (It's unusual taking a SampleList and sending to a SampleList) + if (v.taken.secondsSinceUnixEpoch() <= timeTo) { + runner.template newSample(key,v); + } + } + runner.run(Date(timeTo)); + } + +private: + std::size_t key; + SampleList,Sz> data; +}; + +struct DummyDistanceDelegate /* : herald::analysis::AnalysisDelegate */ { + using value_type = Distance; + + DummyDistanceDelegate() : lastSampledID(0), distances() {}; + DummyDistanceDelegate(const DummyDistanceDelegate&) = delete; // copy ctor deleted + DummyDistanceDelegate(DummyDistanceDelegate&& other) noexcept : lastSampledID(other.lastSampledID), distances(std::move(other.distances)) {} // move ctor + ~DummyDistanceDelegate() {}; + + DummyDistanceDelegate& operator=(DummyDistanceDelegate&& other) noexcept { + lastSampledID = other.lastSampledID; + std::swap(distances,other.distances); + return *this; + } + + // specific override of template + void newSample(SampledID sampled, Sample sample) { + lastSampledID = sampled; + distances.push(sample); + } + + void reset() { + distances.clear(); + lastSampledID = 0; + } + + // Test only methods + SampledID lastSampled() { + return lastSampledID; + } + + const SampleList,25>& samples() { + return distances; + } + +private: + SampledID lastSampledID; + SampleList,25> distances; +}; TEST_CASE("variantset-basic", "[variantset][basic]") { @@ -110,11 +110,11 @@ TEST_CASE("variantset-lists", "[variantset][lists]") { TEST_CASE("variantset-listmanager", "[variantset][listmanager]") { SECTION("variantset-listmanager") { using herald::analysis::ListManager; - herald::analysis::VariantSet,ListManager> vs; + herald::analysis::VariantSet,ListManager> vs; REQUIRE(vs.size() == 2); - ListManager& intValue = vs.template get>(); + ListManager& intValue = vs.template get>(); REQUIRE(intValue.size() == 0); - ListManager& dblValue = vs.template get>(); + ListManager& dblValue = vs.template get>(); REQUIRE(dblValue.size() == 0); SampleList,15>& intList = intValue.list(1234); // list for entity being sampled with SampledId=1234 @@ -126,9 +126,9 @@ TEST_CASE("variantset-listmanager", "[variantset][listmanager]") { dblList.push(Sample(10,14.3)); dblList.push(Sample(20,15.3)); - ListManager& intValue2 = vs.template get>(); + ListManager& intValue2 = vs.template get>(); REQUIRE(intValue2.size() == 1); - ListManager& dblValue2 = vs.template get>(); + ListManager& dblValue2 = vs.template get>(); REQUIRE(dblValue2.size() == 1); SampleList,15>& intList2 = intValue2.list(1234); // list for entity being sampled with SampledId=1234 @@ -146,36 +146,48 @@ TEST_CASE("variantset-listmanager", "[variantset][listmanager]") { /// [Who] As a DCT app developer /// [What] I want to periodically run analysis aggregates automatically /// [Value] So I don't miss any information, and have accurate, regular, samples -// TEST_CASE("analysisrunner-basic", "[analysisrunner][basic]") { -// SECTION("analysisrunner-basic") { -// SampleList,10> srcData{ -// Sample{10,-55},Sample{20,-55},Sample{30,-55},Sample{40,-55},Sample{50,-55}, -// Sample{60,-55},Sample{70,-55},Sample{80,-55},Sample{90,-55},Sample{100,-55} -// }; -// DummyRSSISource src(1234,std::move(srcData)); - -// herald::analysis::algorithms::distance::FowlerBasicAnalyser distanceAnalyser(30, -50, -24); - -// herald::analysis::AnalysisRunner runner; // just for Sample types, and their produced output (Sample) - -// std::shared_ptr myDelegate = std::make_shared(); -// runner.addAnalyser(distanceAnalyser); // so it picks up new data items -// runner.add(myDelegate); // so we receive the relevant output - -// // run at different times and ensure that it only actually runs three times (sample size == 3) -// src.run(20,runner); -// src.run(40,runner); -// src.run(60,runner); -// src.run(80,runner); -// src.run(95,runner); - -// auto& samples = myDelegate->samples(); -// REQUIRE(samples.size() == 3); // didn't reach 4x30 seconds, so no tenth sample -// REQUIRE(samples[0].taken.secondsSinceUnixEpoch() == 30); -// REQUIRE(samples[0].value != 0.0); -// REQUIRE(samples[1].taken.secondsSinceUnixEpoch() == 60); -// REQUIRE(samples[1].value != 0.0); -// REQUIRE(samples[2].taken.secondsSinceUnixEpoch() == 90); -// REQUIRE(samples[2].value != 0.0); -// } -// } \ No newline at end of file +TEST_CASE("analysisrunner-basic", "[analysisrunner][basic]") { + SECTION("analysisrunner-basic") { + SampleList,25> srcData; + srcData.push(10,-55); + srcData.push(20,-55); + srcData.push(30,-55); + srcData.push(40,-55); + srcData.push(50,-55); + srcData.push(60,-55); + srcData.push(70,-55); + srcData.push(80,-55); + srcData.push(90,-55); + srcData.push(100,-55); + DummyRSSISource src(1234,std::move(srcData)); + + herald::analysis::algorithms::distance::FowlerBasicAnalyser distanceAnalyser(30, -50, -24); + + DummyDistanceDelegate myDelegate; + herald::analysis::AnalysisDelegateManager adm(std::move(myDelegate)); // NOTE: myDelegate MOVED FROM and no longer accessible + herald::analysis::AnalysisProviderManager apm(std::move(distanceAnalyser)); // NOTE: distanceAnalyser MOVED FROM and no longer accessible + + herald::analysis::AnalysisRunner< + herald::analysis::AnalysisDelegateManager, + herald::analysis::AnalysisProviderManager, + RSSI,Distance + > runner(adm, apm); // just for Sample types, and their produced output (Sample) + + // run at different times and ensure that it only actually runs three times (sample size == 3) + src.run(20,runner); + src.run(40,runner); // Runs here, because we have data for 10,20,>>30<<,40 <- next run time based on this 'latest' data time + src.run(60,runner); + src.run(80,runner); // Runs here because we have extra data for 50,60,>>70<<,80 <- next run time based on this 'latest' data time + src.run(95,runner); + + auto& delegateRef = adm.get(); + REQUIRE(delegateRef.lastSampled() == 1234); + + auto& samples = delegateRef.samples(); + REQUIRE(samples.size() == 2); // didn't reach 4x30 seconds, so no tenth sample, and didn't run at 60 because previous run was at time 40 + REQUIRE(samples[0].taken.secondsSinceUnixEpoch() == 40); + REQUIRE(samples[0].value != 0.0); + REQUIRE(samples[1].taken.secondsSinceUnixEpoch() == 80); + REQUIRE(samples[1].value != 0.0); + } +} \ No newline at end of file diff --git a/herald/include/herald/analysis/distance_conversion.h b/herald/include/herald/analysis/distance_conversion.h index ecdf38a..8dec191 100644 --- a/herald/include/herald/analysis/distance_conversion.h +++ b/herald/include/herald/analysis/distance_conversion.h @@ -13,6 +13,8 @@ #include "sampling.h" #include "../datatype/distance.h" +#include + namespace herald { namespace analysis { namespace algorithms { @@ -56,12 +58,26 @@ struct FowlerBasic { }; struct FowlerBasicAnalyser { + using input_value_type = RSSI; + using output_value_type = Distance; + + /// default constructor required for array instantiation in manager AnalysisProviderManager + FowlerBasicAnalyser() : interval(10), basic(-11,-0.4), lastRan(0) {} FowlerBasicAnalyser(long interval, double intercept, double coefficient) : interval(interval), basic(intercept, coefficient), lastRan(0) {} ~FowlerBasicAnalyser() = default; - template - void analyse(Date timeNow, const SampleList,SrcSz>& src, std::shared_ptr& dst) { - if (lastRan + interval > timeNow) return; // interval guard + // Generic + // TODO consider removing this annoyance somehow... + template + bool analyse(Date timeNow, SampledID sampled, SampleList,SrcSz>& src, SampleList,DstSz>& dst, CallableForNewSample& callable) { + return false; // no op - compiled out + } + + // Specialisation + template + bool analyse(Date timeNow, SampledID sampled, SampleList,SrcSz>& src, SampleList,DstSz>& dst, CallableForNewSample& callable) { + if (lastRan + interval >= timeNow) return false; // interval guard + std::cout << "RUNNING FOWLER BASIC ANALYSIS at " << timeNow.secondsSinceUnixEpoch() << std::endl; basic.reset(); @@ -74,11 +90,11 @@ struct FowlerBasicAnalyser { auto summary = values | summarise(); - auto mode = summary.get(); - auto var = summary.get(); + auto mode = summary.template get(); + auto var = summary.template get(); auto sd = std::sqrt(var); - auto distance = sl + auto distance = src | herald::analysis::views::filter(valid) | herald::analysis::views::filter( herald::analysis::views::in_range( @@ -88,16 +104,22 @@ struct FowlerBasicAnalyser { ) | aggregate(basic); // type actually - auto agg = distance.get(); + auto agg = distance.template get(); auto d = agg.reduce(); + Date latestTime = values.latest(); + lastRan = latestTime; // TODO move this logic to the caller not the analysis provider + std::cout << "Latest value at time: " << latestTime.secondsSinceUnixEpoch() << std::endl; - dst.newSample(Sample(latestTime,Distance(d))); + Sample newSample((Date)latestTime,Distance(d)); + dst.push(newSample); + callable(sampled,newSample); + return true; } private: - long interval; + TimeInterval interval; FowlerBasic basic; Date lastRan; }; diff --git a/herald/include/herald/analysis/ranges.h b/herald/include/herald/analysis/ranges.h index 5fd47cb..1632c26 100644 --- a/herald/include/herald/analysis/ranges.h +++ b/herald/include/herald/analysis/ranges.h @@ -231,6 +231,14 @@ struct view { return source.end(); } + // auto latest() -> BaseValT { + // //return source.latest(); + // return *(source.end() - 1); + // } + Date latest() { + return (*(source.end() - 1)).taken; + } + template bool operator==(const IterT& other) { return other == source; diff --git a/herald/include/herald/analysis/runner.h b/herald/include/herald/analysis/runner.h index e8dcdff..61963fd 100644 --- a/herald/include/herald/analysis/runner.h +++ b/herald/include/herald/analysis/runner.h @@ -10,101 +10,47 @@ #include #include +// debug only +// #include + namespace herald { namespace analysis { using namespace sampling; -/// \brief Base Interface definition for classes that receive newSample callbacks from AnalysisRunner -struct AnalysisDelegate { - AnalysisDelegate() = default; - virtual ~AnalysisDelegate() = default; - - template - void newSample(SampledID sampled, Sample sample); - - template - void setDestination(RunnerT& runner); -}; - /// \brief Manages a set of lists for a particular Sample Value Type -template +template struct ListManager { using value_type = ValT; + static constexpr std::size_t max_size = Size; - ListManager() noexcept : lists(), nextPos(0) {}; - ListManager(ListManager&& other) : lists(std::move(other.lists)), nextPos(other.nextPos) {}; // move ctor + ListManager() = default; ~ListManager() = default; SampleList,Size>& list(const SampledID sampled) { - for (auto& entry : lists) { - if (entry.key() == sampled) { - return entry.value(); - } - } - auto& val = lists[nextPos++]; - return val.emplace(sampled); + auto iter = lists.try_emplace(sampled).first; + return lists.at(sampled); } void remove(const SampledID listFor) { - // lists.erase(listFor); - // TODO delete value and re-use its allocation space + lists.erase(listFor); } const std::size_t size() const { - return nextPos; + return lists.size(); } -private: - template - struct ListManagerEntry { - ListManagerEntry() : mKey(), mValue() {} - ~ListManagerEntry() = default; - - SampleList,Size>& emplace(const Key k) { - mKey = k; - return value(); - } - - const Key key() const { - return mKey; - } - - SampleList,Size>& value() { - return mValue; - } + decltype(auto) begin() { + return lists.begin(); + } - private: - Key mKey; - SampleList,Size> mValue; - }; + decltype(auto) end() { + return lists.end(); + } - std::array,MaxLists> lists; - int nextPos; +private: + std::map,Size>> lists; }; -// template -// struct ListManager { -// using value_type = ValT; - -// ListManager() = default; -// ~ListManager() = default; - -// SampleList,Size>& list(const SampledID sampled) { -// auto iter = lists.try_emplace(sampled).first; -// return lists.at(sampled); -// } - -// void remove(const SampledID listFor) { -// lists.erase(listFor); -// } - -// const std::size_t size() const { -// return lists.size(); -// } - -// private: -// std::map,Size>> lists; -// }; /// \brief A fixed size set that holds exactly one instance of the std::variant for each /// of the specified ValTs value types. @@ -132,6 +78,14 @@ struct VariantSet { return variants.size(); } + decltype(auto) begin() { + return variants.begin(); + } + + decltype(auto) end() { + return variants.end(); + } + private: std::array,Size> variants; template @@ -146,87 +100,191 @@ struct VariantSet { } }; -/// The below is an example ValueSource... -/// template -/// struct ExValueSource { -/// using value_type = ValT; -/// -/// template -/// void setDestination(RunnerT& runner) { -/// // save reference -/// } -/// -/// // At some predetermined point external to the analyser runner -/// // this ValueSource will call runner.newSample(Sample sample) -/// }; +/// \brief Convenience wrapper for all AnalysisDelegate types used by the analysis API +template +struct AnalysisDelegateManager { + AnalysisDelegateManager(DelegateTypes... dts) : delegates() { + addDelegates(0,dts...); + } + ~AnalysisDelegateManager() = default; + + template + void notify(SampledID sampled, Sample sample) { + for (auto& delegateV : delegates) { + std::visit([sampled,sample](auto&& arg) { + using noref = typename std::remove_reference::type; + if constexpr (std::is_same_v) { + ((decltype(arg))arg).newSample(sampled,sample); // cast to call derived class function + } + }, delegateV); + } + } + + /// CAN THROW std::bad_variant_access + template + DelegateT& get() { + for (auto& v : delegates) { + if (auto pval = std::get_if(&v)) { + return *pval; + } + } + throw std::bad_variant_access(); + } + +private: + std::array,sizeof...(DelegateTypes)> delegates; + + template + constexpr void addDelegates(int nextPos,LastT&& last) { + delegates[nextPos] = (std::move(last)); + } + + template + constexpr void addDelegates(int nextPos,FirstT&& first, SecondT&& second, RestT&&... rest) { + delegates[nextPos] = std::move(first); + ++nextPos; + addDelegates(nextPos,second,rest...); + } +}; + +/// \brief Convenience wrapper for all AnalysisProvider types used by the analysis API +template +struct AnalysisProviderManager { + AnalysisProviderManager(ProviderTypes... prvs) : providers() { + addProviders(0, prvs...); + } + ~AnalysisProviderManager() = default; + + template + bool analyse(Date timeNow, SampledID sampled, SampleList,SrcSz>& src, ListManager& lists, CallableForNewSample& callable) { + bool generated = false; + for (auto& providerV : providers) { + std::visit([&timeNow,&sampled,&src,&lists,&generated,&callable](auto&& arg) { + using noref = typename std::remove_reference::type; + // Ensure our calee supports the types we have + if constexpr (std::is_same_v) { + auto& listRef = lists.list(sampled); + generated = generated | ((decltype(arg))arg).analyse(timeNow,sampled,src,listRef,callable); + } + }, providerV); + } + return generated; + } + + /// CAN THROW std::bad_variant_access + template + ProviderT& get() { + for (auto& v : providers) { + if (auto pval = std::get_if(&v)) { + return *pval; + } + } + throw std::bad_variant_access(); + } + + template + constexpr bool hasMatchingAnalyser() noexcept { + bool match = false; + for (auto& providerV : providers) { + std::visit([&match] (auto&& provider) { + using InputValT = typename InputT::value_type; + using InT = typename std::remove_reference_t::input_value_type; + using OutT = typename std::remove_reference_t::output_value_type; + // std::cout << " Provider being checked " << typeid(provider).name() << std::endl; + // InT inInstance; + // OutT outInstance; + // std::cout << " In type " << typeid(inInstance).name() << ", out type " << typeid(outInstance).name() << std::endl; + // InputValT inputInstance; + // OutputT outputInstance; + // std::cout << " Input type " << typeid(inputInstance).name() << ", output type " << typeid(outputInstance).name() << std::endl; + if constexpr (std::is_same_v && std::is_same_v) { + match = true; + // std::cout << " MATCHED!" << std::endl; + } + }, providerV); + } + return match; + } + +private: + std::array,sizeof...(ProviderTypes)> providers; + + template + constexpr void addProviders(int nextPos, LastT&& last) { + providers[nextPos] = std::move(last); + } + + template + constexpr void addProviders(int nextPos, FirstT&& first, SecondT&& second, RestT&&... rest) { + providers[nextPos] = std::move(first); + ++nextPos; + addProviders(nextPos,second,rest...); + } +}; /// \brief Manages all sample lists, sources, sinks, and analysis instances for all data generated within a system /// /// This class can be used 'live' against real sensors, or statically with reference data. /// This is achieved by ensuring the run(Date) method takes in the Date for the time of evaluation rather /// than using the current Date. -// template -// struct AnalysisRunner { -// // using valueTypes = (typename SourceTypes::value_type)...; - -// AnalysisRunner(/*SourceTypes&... sources*/) : lists(), notifiables() { -// // registerRunner(sources...); -// } -// ~AnalysisRunner() = default; - -// /// We are an analysis delegate ourselves - this is used by Source types, and by producers (analysis runners) -// template -// void newSample(SampledID sampled, sampling::Sample sample) { -// // incoming sample. Pass to correct list -// lists.template get>().list(sampled)->second.push(sample); -// } - -// /// Run the relevant analyses given the current time point -// void run(Date timeNow) { -// // call analyse(dateNow,srcList,dstDelegate) for all delegates with the correct list each, for each sampled -// // TODO performance enhancement - 'dirty' sample lists only (ones with new data) -// for (auto& listManager : lists) { -// using ValT = listManager::value_type; -// for (auto& delegate : notifiables) { -// // if constexpr (std::is_same_v) { // SHOULD BE RUNNERS NOT DELEGATES -// //delegate. -// // } -// } -// } -// } - -// void add(std::shared_ptr delegate) { -// notifiables.push_back(delegate); -// } - -// template -// void addAnalyser(AnalyserT& analyser) { -// // TODO fill this out and call its analyse() function when required -// } - -// // /// callback from analysis data source -// // template -// // SampleList>& list(const SampledID sampled) { -// // return lists.get().list(sampled); -// // } - -// private: -// // TODO make sizes a parameterised list derived from template parameters -// VariantSet...> lists; // exactly one list manager per value type -// std::vector> notifiables; // more than one delegate per value type -// //std::vector< // runners - -// // template -// // void registerRunner(FirstT first) { -// // first.setDestination(*this); -// // } - -// // template -// // void registerRunner(FirstT first,SecondT second,RestT... rest) { -// // first.setDestination(*this); -// // registerRunner(second,rest...); -// // } -// }; +template // TODO derive SourceTypes from providers and delegates // TODO parameterise type lengths somehow (traits template?) +struct AnalysisRunner { + static constexpr std::size_t ListSize = 25; // TODO make this external somehow for each type (trait?) + // using valueTypes = (typename SourceTypes::value_type)...; + + AnalysisRunner(AnalysisDelegateManagerT& adm, AnalysisProviderManagerT& provds) : lists(), delegates(adm), runners(provds) {} + ~AnalysisRunner() = default; + + /// We are an analysis delegate ourselves - this is used by Source types, and by producers (analysis runners) + template + void newSample(SampledID sampled, sampling::Sample sample) { + // incoming sample. Pass to correct list + lists.template get>().list(sampled).push(sample); // TODO get ListSize dynamically + // inform delegates + delegates.notify(sampled,sample); + } + + template + void operator()(SampledID sampled,sampling::Sample sample) { + newSample(sampled,sample); + } + + /// Run the relevant analyses given the current time point + void run(Date timeNow) { + // call analyse(dateNow,srcList,dstDelegate) for all delegates with the correct list each, for each sampled + // TODO performance enhancement - 'dirty' sample lists only (ones with new data) + for (auto& listManager : lists) { // For each input list + std::visit([timeNow,this] (auto&& arg) { // Visit each of our list managers (that may be used as an output list) + for (auto& mgrPair : arg) { // For each output list instance // arg = ListManager,SrcSz> + // Derived Input type and size from 'list' input list + + auto& sampled = mgrPair.first; + auto& list = mgrPair.second; + + for (auto& outputListManagerV : lists) { + std::visit([timeNow, &list, &sampled, this] (auto&& outputListManager) { // Visit each of our list managers (that may be used as an output list) + using InputValT = typename std::remove_reference_t::value_type; + using LMValT = typename std::remove_reference_t::value_type; + // std::cout << "Trying to call analysers for source type " << typeid(list).name() << " to output type " << typeid(outputListManager).name() << std::endl; + + // Check for presence of an analyser that converts from InputValT to LMValT + if (runners.template hasMatchingAnalyser()) { + // std::cout << "Found matching analyser!" << std::endl; + /*bool newDataGenerated =*/ runners.template analyse(timeNow,sampled,list,outputListManager, *this); // + } + }, outputListManagerV); + } + } + }, listManager); + } + } + +private: + // TODO make sizes a parameterised list derived from template parameters + VariantSet...> lists; // exactly one list manager per value type + AnalysisDelegateManagerT& delegates; + AnalysisProviderManagerT& runners; +}; } } diff --git a/herald/include/herald/analysis/sampling.h b/herald/include/herald/analysis/sampling.h index 218d7f9..8b3f5e8 100644 --- a/herald/include/herald/analysis/sampling.h +++ b/herald/include/herald/analysis/sampling.h @@ -111,6 +111,13 @@ struct SampleList { SampleList(const SampleList&) = delete; // no shallow copies allowed SampleList(SampleList&& other) noexcept : data(std::move(other.data)), oldestPosition(other.oldestPosition), newestPosition(other.newestPosition) {} // move ctor + SampleList& operator=(SampleList&& other) noexcept { + std::swap(data,other.data); + oldestPosition = other.oldestPosition; + newestPosition = other.newestPosition; + return *this; + } + // Creates a list from static initialiser list elements, using deduction guide template SampleList(MultiSampleT... initialiserElements) : data(), oldestPosition(SIZE_MAX), newestPosition(SIZE_MAX) { @@ -179,6 +186,13 @@ struct SampleList { newestPosition = SIZE_MAX; } + // SampleT latest() { + // return data[newestPosition]; + // } + Date latest() { + return data[newestPosition].taken; + } + SampleIterator> begin() { return SampleIterator>(*this); } From 604764de3f0475d6137138996c2c1de690643e1a Mon Sep 17 00:00:00 2001 From: Adam Fowler Date: Sat, 20 Mar 2021 17:15:22 +0000 Subject: [PATCH 09/28] Related to #24. PIMPL removed from datatype classes Signed-off-by: Adam Fowler --- CMakeLists.txt | 2 +- herald-tests/analysisrunner-tests.cpp | 4 + .../herald/analysis/distance_conversion.h | 7 +- herald/include/herald/data/contact_log.h | 3 +- herald/include/herald/datatype/encounter.h | 9 +- herald/include/herald/datatype/location.h | 21 +++-- herald/include/herald/datatype/rssi.h | 3 +- .../herald/datatype/target_identifier.h | 3 +- .../include/herald/datatype/time_interval.h | 3 +- herald/include/herald/datatype/uuid.h | 6 +- .../include/herald/default_sensor_delegate.h | 3 +- herald/include/herald/sensor_delegate.h | 3 +- herald/src/data/contact_log.cpp | 3 +- herald/src/datatype/encounter.cpp | 66 +++++++-------- herald/src/datatype/location.cpp | 70 ++++++++-------- herald/src/datatype/rssi.cpp | 58 ++++++------- herald/src/datatype/target_identifier.cpp | 68 +++++++-------- herald/src/datatype/time_interval.cpp | 82 +++++++++---------- herald/src/datatype/uuid.cpp | 60 +++++++------- herald/src/default_sensor_delegate.cpp | 3 +- 20 files changed, 250 insertions(+), 227 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index e879025..2f48964 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.12) -project(herald VERSION 1.2.0 LANGUAGES CXX) +project(herald VERSION 1.3.0 LANGUAGES CXX) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) diff --git a/herald-tests/analysisrunner-tests.cpp b/herald-tests/analysisrunner-tests.cpp index a02ee0b..3390b2e 100644 --- a/herald-tests/analysisrunner-tests.cpp +++ b/herald-tests/analysisrunner-tests.cpp @@ -7,6 +7,7 @@ #include "herald/herald.h" #include +#include using namespace herald::analysis::sampling; using namespace herald::datatype; @@ -189,5 +190,8 @@ TEST_CASE("analysisrunner-basic", "[analysisrunner][basic]") { REQUIRE(samples[0].value != 0.0); REQUIRE(samples[1].taken.secondsSinceUnixEpoch() == 80); REQUIRE(samples[1].value != 0.0); + + // Let's see the total memory in use... + std::cout << "AnalysisRunner::RAM = " << sizeof(runner) << std::endl; } } \ No newline at end of file diff --git a/herald/include/herald/analysis/distance_conversion.h b/herald/include/herald/analysis/distance_conversion.h index 8dec191..0c5a44b 100644 --- a/herald/include/herald/analysis/distance_conversion.h +++ b/herald/include/herald/analysis/distance_conversion.h @@ -13,7 +13,8 @@ #include "sampling.h" #include "../datatype/distance.h" -#include +// Debug only +// #include namespace herald { namespace analysis { @@ -77,7 +78,7 @@ struct FowlerBasicAnalyser { template bool analyse(Date timeNow, SampledID sampled, SampleList,SrcSz>& src, SampleList,DstSz>& dst, CallableForNewSample& callable) { if (lastRan + interval >= timeNow) return false; // interval guard - std::cout << "RUNNING FOWLER BASIC ANALYSIS at " << timeNow.secondsSinceUnixEpoch() << std::endl; + // std::cout << "RUNNING FOWLER BASIC ANALYSIS at " << timeNow.secondsSinceUnixEpoch() << std::endl; basic.reset(); @@ -110,7 +111,7 @@ struct FowlerBasicAnalyser { Date latestTime = values.latest(); lastRan = latestTime; // TODO move this logic to the caller not the analysis provider - std::cout << "Latest value at time: " << latestTime.secondsSinceUnixEpoch() << std::endl; + // std::cout << "Latest value at time: " << latestTime.secondsSinceUnixEpoch() << std::endl; Sample newSample((Date)latestTime,Distance(d)); dst.push(newSample); diff --git a/herald/include/herald/data/contact_log.h b/herald/include/herald/data/contact_log.h index ce208d9..4c5f7fa 100644 --- a/herald/include/herald/data/contact_log.h +++ b/herald/include/herald/data/contact_log.h @@ -25,7 +25,8 @@ class ErrorStreamContactLogger : public SensorDelegate { void sensor(SensorType sensor, const ImmediateSendData& didReceive, const TargetIdentifier& fromTarget) override; void sensor(SensorType sensor, const std::vector& didShare, const TargetIdentifier& fromTarget) override; void sensor(SensorType sensor, const Proximity& didMeasure, const TargetIdentifier& fromTarget) override; - void sensor(SensorType sensor, const Location& didVisit) override; + template + void sensor(SensorType sensor, const Location& didVisit); void sensor(SensorType sensor, const Proximity& didMeasure, const TargetIdentifier& fromTarget, const PayloadData& withPayload) override; void sensor(SensorType sensor, const SensorState& didUpdateState) override; diff --git a/herald/include/herald/datatype/encounter.h b/herald/include/herald/datatype/encounter.h index 5fb38d8..8171160 100644 --- a/herald/include/herald/datatype/encounter.h +++ b/herald/include/herald/datatype/encounter.h @@ -30,8 +30,13 @@ class Encounter { const Date& timestamp() const; private: - class Impl; - std::unique_ptr mImpl; // PIMPL IDIOM + // class Impl; + // std::unique_ptr mImpl; // PIMPL IDIOM + + Date date; + Proximity prox; + PayloadData payloadData; + bool valid; }; diff --git a/herald/include/herald/datatype/location.h b/herald/include/herald/datatype/location.h index 66b881d..e923fb7 100644 --- a/herald/include/herald/datatype/location.h +++ b/herald/include/herald/datatype/location.h @@ -14,17 +14,28 @@ namespace herald { namespace datatype { +template class Location { public: - Location(std::shared_ptr value, Date start, Date end); + Location(LocationReferenceT&& value, Date&& start, Date&& end) + : mValue(std::move(value)),mStart(std::move(start)),mEnd(std::move(end)) + {}; ~Location(); - std::string description() const; - operator std::string() const noexcept; + std::string description() const { + return mValue->description() + ":[from=" + ((std::string)mStart) + ",to=" + ((std::string)mEnd) + "]"; + } + + operator std::string() const noexcept { + return description(); + } private: - class Impl; - std::unique_ptr mImpl; // PIMPL IDIOM + // class Impl; + // std::unique_ptr mImpl; // PIMPL IDIOM + LocationReferenceT mValue; + Date mStart; + Date mEnd; }; } // end namespace diff --git a/herald/include/herald/datatype/rssi.h b/herald/include/herald/datatype/rssi.h index 57e7c43..5ca1d19 100644 --- a/herald/include/herald/datatype/rssi.h +++ b/herald/include/herald/datatype/rssi.h @@ -41,8 +41,7 @@ class RSSI { int intValue() const noexcept; private: - class Impl; - std::unique_ptr mImpl; + int value; }; } // end namespace diff --git a/herald/include/herald/datatype/target_identifier.h b/herald/include/herald/datatype/target_identifier.h index c747c34..4b6a2c9 100644 --- a/herald/include/herald/datatype/target_identifier.h +++ b/herald/include/herald/datatype/target_identifier.h @@ -36,8 +36,7 @@ class TargetIdentifier { operator Data() const; private: - class Impl; - std::unique_ptr mImpl; + Data value; }; diff --git a/herald/include/herald/datatype/time_interval.h b/herald/include/herald/datatype/time_interval.h index 92cbae0..59aac07 100644 --- a/herald/include/herald/datatype/time_interval.h +++ b/herald/include/herald/datatype/time_interval.h @@ -58,8 +58,7 @@ class TimeInterval { operator long() const noexcept; // returns SECONDS not millis private: - class Impl; - std::unique_ptr mImpl; + long secs; }; } // end namespace diff --git a/herald/include/herald/datatype/uuid.h b/herald/include/herald/datatype/uuid.h index f77909b..daf73b5 100644 --- a/herald/include/herald/datatype/uuid.h +++ b/herald/include/herald/datatype/uuid.h @@ -9,7 +9,7 @@ #include "randomness.h" #include -#include +#include namespace herald { namespace datatype { @@ -42,8 +42,8 @@ class UUID { std::string string() const noexcept; private: - class Impl; - std::unique_ptr mImpl; + std::array mData = { {0}}; + bool mValid; UUID(std::array data, bool isValid) noexcept; }; diff --git a/herald/include/herald/default_sensor_delegate.h b/herald/include/herald/default_sensor_delegate.h index 94c4dcc..8b119b7 100644 --- a/herald/include/herald/default_sensor_delegate.h +++ b/herald/include/herald/default_sensor_delegate.h @@ -34,7 +34,8 @@ class DefaultSensorDelegate : public SensorDelegate { void sensor(SensorType sensor, const Proximity& didMeasure, const TargetIdentifier& fromTarget) override; /// Detection of time spent at location, e.g. at specific restaurant between 02/06/2020 19:00 and 02/06/2020 21:00 - void sensor(SensorType sensor, const Location& didVisit) override; + template + void sensor(SensorType sensor, const Location& didVisit); /// Measure proximity to target with payload data. Combines didMeasure and didRead into a single convenient delegate method void sensor(SensorType sensor, const Proximity& didMeasure, const TargetIdentifier& fromTarget, const PayloadData& withPayload) override; diff --git a/herald/include/herald/sensor_delegate.h b/herald/include/herald/sensor_delegate.h index 69b093a..739e372 100644 --- a/herald/include/herald/sensor_delegate.h +++ b/herald/include/herald/sensor_delegate.h @@ -42,7 +42,8 @@ class SensorDelegate { virtual void sensor(SensorType sensor, const Proximity& didMeasure, const TargetIdentifier& fromTarget) = 0; /// Detection of time spent at location, e.g. at specific restaurant between 02/06/2020 19:00 and 02/06/2020 21:00 - virtual void sensor(SensorType sensor, const Location& didVisit) = 0; + template + void sensor(SensorType sensor, const Location& didVisit) {} /// Measure proximity to target with payload data. Combines didMeasure and didRead into a single convenient delegate method virtual void sensor(SensorType sensor, const Proximity& didMeasure, const TargetIdentifier& fromTarget, const PayloadData& withPayload) = 0; diff --git a/herald/src/data/contact_log.cpp b/herald/src/data/contact_log.cpp index ba28742..443b2a0 100644 --- a/herald/src/data/contact_log.cpp +++ b/herald/src/data/contact_log.cpp @@ -140,8 +140,9 @@ ErrorStreamContactLogger::sensor(SensorType sensor, const Proximity& didMeasure, // HERR(mImpl->timestamp() + "," + str(sensor) + "," + mImpl->csv((std::string)fromTarget) + ",,,3,,," + mImpl->csv((std::string)didMeasure)); } +template void -ErrorStreamContactLogger::sensor(SensorType sensor, const Location& didVisit) +ErrorStreamContactLogger::sensor(SensorType sensor, const Location& didVisit) { // std::string s = mImpl->timestamp(); // s += ","; diff --git a/herald/src/datatype/encounter.cpp b/herald/src/datatype/encounter.cpp index 8639d5c..6610202 100644 --- a/herald/src/datatype/encounter.cpp +++ b/herald/src/datatype/encounter.cpp @@ -10,54 +10,54 @@ namespace herald { namespace datatype { // PIMPL DEFINITION -class Encounter::Impl { -public: - Impl(); - Impl(Proximity didMeasure, PayloadData withPayload, Date timestamp); - ~Impl() = default; - - Date date; - Proximity proximity; - PayloadData payloadData; - bool valid; -}; - - -// PIMPL DECLARATIONS -Encounter::Impl::Impl() - : date(), proximity(), payloadData(), valid(false) -{ - ; -} - -Encounter::Impl::Impl(Proximity didMeasure, PayloadData withPayload, Date timestamp) - : date(timestamp), proximity(didMeasure), payloadData(withPayload), valid(true) -{ - ; -} +// class Encounter::Impl { +// public: +// Impl(); +// Impl(Proximity didMeasure, PayloadData withPayload, Date timestamp); +// ~Impl() = default; + +// Date date; +// Proximity proximity; +// PayloadData payloadData; +// bool valid; +// }; + + +// // PIMPL DECLARATIONS +// Encounter::Impl::Impl() +// : date(), proximity(), payloadData(), valid(false) +// { +// ; +// } + +// Encounter::Impl::Impl(Proximity didMeasure, PayloadData withPayload, Date timestamp) +// : date(timestamp), proximity(didMeasure), payloadData(withPayload), valid(true) +// { +// ; +// } // ENCOUNTER DECLARATIONS Encounter::Encounter(Proximity didMeasure, PayloadData withPayload, Date timestamp) - : mImpl(std::make_unique(didMeasure,withPayload,timestamp)) + : date(timestamp), prox(didMeasure), payloadData(withPayload), valid(true) { ; } Encounter::Encounter(Proximity didMeasure, PayloadData withPayload) - : mImpl(std::make_unique(didMeasure,withPayload,Date())) + : date(Date()), prox(didMeasure), payloadData(withPayload), valid(true) { ; } Encounter::Encounter(const std::string csvRow) - : mImpl(std::make_unique()) + : date(), prox(), payloadData(), valid(false) { ; // TODO parse the csv } -Encounter::~Encounter() {} +Encounter::~Encounter() = default; std::string @@ -67,25 +67,25 @@ Encounter::csvString() const { bool Encounter::isValid() const { - return mImpl->valid; + return valid; } const Proximity& Encounter::proximity() const { - return mImpl->proximity; + return prox; } const PayloadData& Encounter::payload() const { - return mImpl->payloadData; + return payloadData; } const Date& Encounter::timestamp() const { - return mImpl->date; + return date; } } // end namespace diff --git a/herald/src/datatype/location.cpp b/herald/src/datatype/location.cpp index 7bf5a5b..42a4f98 100644 --- a/herald/src/datatype/location.cpp +++ b/herald/src/datatype/location.cpp @@ -13,48 +13,50 @@ namespace datatype { // IMPL DEFINITION -class Location::Impl { -public: - Impl(std::shared_ptr value, Date start, Date end); - ~Impl() = default; +// class Location::Impl { +// public: +// Impl(std::shared_ptr value, Date start, Date end); +// ~Impl() = default; - // member variables - std::shared_ptr mValue; - Date mStart; - Date mEnd; -}; +// // member variables +// std::shared_ptr mValue; +// Date mStart; +// Date mEnd; +// }; -// IMPL DECLARATIONS -Location::Impl::Impl(std::shared_ptr value, Date start, Date end) - : mValue(value), - mStart(std::move(start)), - mEnd(std::move(end)) -{ - ; -} +// // IMPL DECLARATIONS +// Location::Impl::Impl(std::shared_ptr value, Date start, Date end) +// : mValue(value), +// mStart(std::move(start)), +// mEnd(std::move(end)) +// { +// ; +// } // LOCATION DECLARATIONS -Location::Location(std::shared_ptr value, Date start, Date end) - : mImpl(std::make_unique(value,start,end)) -{ - ; -} - -Location::~Location() {} - -std::string -Location::description() const { - return mImpl->mValue->description() + ":[from=" + ((std::string)mImpl->mStart) + ",to=" + ((std::string)mImpl->mEnd) + "]"; -} - -Location::operator std::string() const noexcept -{ - return description(); -} +// Location::Location(std::shared_ptr value, Date start, Date end) +// : mValue(value), +// mStart(std::move(start)), +// mEnd(std::move(end)) +// { +// ; +// } + +// Location::~Location() {} + +// std::string +// Location::description() const { +// return mValue->description() + ":[from=" + ((std::string)mStart) + ",to=" + ((std::string)mEnd) + "]"; +// } + +// Location::operator std::string() const noexcept +// { +// return description(); +// } diff --git a/herald/src/datatype/rssi.cpp b/herald/src/datatype/rssi.cpp index 5a10e12..5a0d63e 100644 --- a/herald/src/datatype/rssi.cpp +++ b/herald/src/datatype/rssi.cpp @@ -9,38 +9,38 @@ namespace herald { namespace datatype { -class RSSI::Impl { -public: - Impl(); - ~Impl() = default; +// class RSSI::Impl { +// public: +// Impl(); +// ~Impl() = default; - int value; -}; +// int value; +// }; -RSSI::Impl::Impl() : value(0) { } +// RSSI::Impl::Impl() : value(0) { } RSSI::RSSI() - : mImpl(std::make_unique()) + : value(0) { ; } RSSI::RSSI(int value) - : mImpl(std::make_unique()) + : value(value) { - mImpl->value = value; + ; } RSSI::RSSI(const RSSI& other) - : mImpl(std::make_unique()) + : value(other.value) { - mImpl->value = other.mImpl->value; + ; } RSSI::RSSI(RSSI&& other) - : mImpl(std::make_unique()) + : value(other.value) { - mImpl->value = other.mImpl->value; + ; } RSSI::~RSSI() {} @@ -48,37 +48,37 @@ RSSI::~RSSI() {} RSSI& RSSI::operator=(const RSSI& other) { - mImpl->value = other.mImpl->value; + value = other.value; return *this; } RSSI& RSSI::operator=(RSSI&& other) { - mImpl->value = other.mImpl->value; + value = other.value; return *this; } std::size_t RSSI::hashCode() const noexcept { - return std::hash{}(mImpl->value); + return std::hash{}(value); } RSSI::operator std::string() const noexcept { - return "RSSI{value=" + std::to_string(mImpl->value) + "}"; + return "RSSI{value=" + std::to_string(value) + "}"; } int RSSI::intValue() const noexcept { - return mImpl->value; + return value; } RSSI::operator long() const noexcept { - return mImpl->value; + return value; } RSSI::operator double() const noexcept { - return (double)mImpl->value; + return (double)value; } @@ -86,49 +86,49 @@ RSSI::operator double() const noexcept { bool RSSI::operator==(const int other) const noexcept { - return mImpl->value == other; + return value == other; } bool RSSI::operator!=(const int other) const noexcept { - return mImpl->value != other; + return value != other; } bool RSSI::operator==(const RSSI& other) const noexcept { - return mImpl->value == other.mImpl->value; + return value == other.value; } bool RSSI::operator!=(const RSSI& other) const noexcept { - return mImpl->value != other.mImpl->value; + return value != other.value; } bool RSSI::operator<(const RSSI& other) const noexcept { - return mImpl->value < other.mImpl->value; + return value < other.value; } bool RSSI::operator<=(const RSSI& other) const noexcept { - return mImpl->value <= other.mImpl->value; + return value <= other.value; } bool RSSI::operator>(const RSSI& other) const noexcept { - return mImpl->value > other.mImpl->value; + return value > other.value; } bool RSSI::operator>=(const RSSI& other) const noexcept { - return mImpl->value >= other.mImpl->value; + return value >= other.value; } diff --git a/herald/src/datatype/target_identifier.cpp b/herald/src/datatype/target_identifier.cpp index f713ea1..b8faa4f 100644 --- a/herald/src/datatype/target_identifier.cpp +++ b/herald/src/datatype/target_identifier.cpp @@ -8,33 +8,33 @@ namespace herald { namespace datatype { -class TargetIdentifier::Impl { -public: - Impl(); - Impl(const Data& mac); - Impl(const TargetIdentifier& from); - ~Impl() = default; +// class TargetIdentifier::Impl { +// public: +// Impl(); +// Impl(const Data& mac); +// Impl(const TargetIdentifier& from); +// ~Impl() = default; - Data value; -}; +// Data value; +// }; -TargetIdentifier::Impl::Impl() - : value() -{ - ; -} +// TargetIdentifier::Impl::Impl() +// : value() +// { +// ; +// } -TargetIdentifier::Impl::Impl(const Data& v) - : value(v) -{ - ; -} +// TargetIdentifier::Impl::Impl(const Data& v) +// : value(v) +// { +// ; +// } -TargetIdentifier::Impl::Impl(const TargetIdentifier& v) - : value((Data)v) // conversion operator -{ - ; -} +// TargetIdentifier::Impl::Impl(const TargetIdentifier& v) +// : value((Data)v) // conversion operator +// { +// ; +// } @@ -43,19 +43,19 @@ TargetIdentifier::Impl::Impl(const TargetIdentifier& v) TargetIdentifier::TargetIdentifier() - : mImpl(std::make_unique()) + : value() { ; // TODO set value to random v4 UUID string } TargetIdentifier::TargetIdentifier(const Data& data) - : mImpl(std::make_unique(data)) + : value(data) { ; } TargetIdentifier::TargetIdentifier(const TargetIdentifier& from) - : mImpl(std::make_unique(from)) + : value((Data)from) { ; } @@ -65,7 +65,7 @@ TargetIdentifier::~TargetIdentifier() {} TargetIdentifier& TargetIdentifier::operator=(const TargetIdentifier& from) { - mImpl->value = from.mImpl->value; + value = from.value; return *this; } @@ -76,7 +76,7 @@ TargetIdentifier::operator==(const TargetIdentifier& other) const noexcept { bool TargetIdentifier::operator==(const Data& other) const noexcept { - return mImpl->value == other; + return value == other; } bool @@ -86,29 +86,29 @@ TargetIdentifier::operator!=(const TargetIdentifier& other) const noexcept { bool TargetIdentifier::operator!=(const Data& other) const noexcept { - return mImpl->value != other; + return value != other; } bool TargetIdentifier::operator<(const TargetIdentifier& other) const noexcept { - return mImpl->value < other.mImpl->value; + return value < other.value; } bool TargetIdentifier::operator>(const TargetIdentifier& other) const noexcept { - return mImpl->value > other.mImpl->value; + return value > other.value; } std::size_t TargetIdentifier::hashCode() const { - return std::hash{}(mImpl->value); + return std::hash{}(value); } TargetIdentifier::operator std::string() const { - return mImpl->value.description(); + return value.description(); } TargetIdentifier::operator Data() const { - return mImpl->value; + return value; } } // end namespace diff --git a/herald/src/datatype/time_interval.cpp b/herald/src/datatype/time_interval.cpp index 448936a..e9d93a2 100644 --- a/herald/src/datatype/time_interval.cpp +++ b/herald/src/datatype/time_interval.cpp @@ -7,18 +7,18 @@ namespace herald { namespace datatype { -class TimeInterval::Impl { -public: - Impl(); - Impl(long secondsSinceUnixEpoch); - ~Impl() = default; +// class TimeInterval::Impl { +// public: +// Impl(); +// Impl(long secondsSinceUnixEpoch); +// ~Impl() = default; - long seconds; -}; +// long seconds; +// }; -TimeInterval::Impl::Impl() : seconds(0) { } +// TimeInterval::Impl::Impl() : seconds(0) { } -TimeInterval::Impl::Impl(long secondsSinceUnixEpoch) : seconds(secondsSinceUnixEpoch) { } +// TimeInterval::Impl::Impl(long secondsSinceUnixEpoch) : seconds(secondsSinceUnixEpoch) { } @@ -46,27 +46,27 @@ TimeInterval::zero() { TimeInterval::TimeInterval(long seconds) - : mImpl(std::make_unique(seconds)) + : secs(seconds) { ; } TimeInterval::TimeInterval(const Date& date) - : mImpl(std::make_unique()) + : secs(date.secondsSinceUnixEpoch()) { - mImpl->seconds = date.secondsSinceUnixEpoch(); + ; } TimeInterval::TimeInterval(const Date& from, const Date& to) - : mImpl(std::make_unique()) + : secs(to.secondsSinceUnixEpoch() - from.secondsSinceUnixEpoch()) { - mImpl->seconds = to.secondsSinceUnixEpoch() - from.secondsSinceUnixEpoch(); + ; } TimeInterval::TimeInterval(const TimeInterval& other) - : mImpl(std::make_unique()) + : secs(other.secs) { - mImpl->seconds = other.mImpl->seconds; + ; } TimeInterval::~TimeInterval() {} @@ -75,49 +75,49 @@ TimeInterval::~TimeInterval() {} TimeInterval& TimeInterval::operator=(const TimeInterval& other) noexcept { - mImpl->seconds = other.mImpl->seconds; + secs = other.secs; return *this; } TimeInterval TimeInterval::operator*(const TimeInterval& other) noexcept { - return TimeInterval(mImpl->seconds * other.mImpl->seconds); + return TimeInterval(secs * other.secs); } TimeInterval TimeInterval::operator+(const TimeInterval& other) noexcept { - return TimeInterval(mImpl->seconds + other.mImpl->seconds); + return TimeInterval(secs + other.secs); } TimeInterval& TimeInterval::operator+=(const TimeInterval& other) noexcept { - mImpl->seconds += other.mImpl->seconds; + secs += other.secs; return *this; } TimeInterval TimeInterval::operator-(const TimeInterval& other) noexcept { - return TimeInterval(mImpl->seconds - other.mImpl->seconds); + return TimeInterval(secs - other.secs); } TimeInterval& TimeInterval::operator-=(const TimeInterval& other) noexcept { - mImpl->seconds -= other.mImpl->seconds; + secs -= other.secs; return *this; } TimeInterval TimeInterval::operator*(double multiplier) noexcept { - auto output = mImpl->seconds * multiplier; - return TimeInterval(static_castseconds)>(output)); + auto output = secs * multiplier; + return TimeInterval(static_cast(output)); } TimeInterval& TimeInterval::operator*=(double multiplier) noexcept { - auto output = mImpl->seconds * multiplier; - mImpl->seconds = static_castseconds)>(output); + auto output = secs * multiplier; + secs = static_cast(output); return *this; } @@ -127,8 +127,8 @@ TimeInterval::operator/(double divisor) noexcept if (0 == divisor) { return *this; } - auto output = mImpl->seconds / divisor; - return TimeInterval(static_castseconds)>(output)); + auto output = secs / divisor; + return TimeInterval(static_cast(output)); } TimeInterval& @@ -137,8 +137,8 @@ TimeInterval::operator/=(double divisor) noexcept if (0 == divisor) { return *this; } - auto output = mImpl->seconds / divisor; - mImpl->seconds = static_castseconds)>(output); + auto output = secs / divisor; + secs = static_cast(output); return *this; } @@ -150,57 +150,57 @@ TimeInterval::operator long() const noexcept { bool TimeInterval::operator>(const TimeInterval& other) const noexcept { - return mImpl->seconds > other.mImpl->seconds; + return secs > other.secs; } bool TimeInterval::operator>=(const TimeInterval& other) const noexcept { - return mImpl->seconds >= other.mImpl->seconds; + return secs >= other.secs; } bool TimeInterval::operator<(const TimeInterval& other) const noexcept { - return mImpl->seconds < other.mImpl->seconds; + return secs < other.secs; } bool TimeInterval::operator<=(const TimeInterval& other) const noexcept { - return mImpl->seconds <= other.mImpl->seconds; + return secs <= other.secs; } bool TimeInterval::operator==(const TimeInterval& other) const noexcept { - return mImpl->seconds == other.mImpl->seconds; + return secs == other.secs; } bool TimeInterval::operator!=(const TimeInterval& other) const noexcept { - return mImpl->seconds != other.mImpl->seconds; + return secs != other.secs; } long TimeInterval::millis() const noexcept { - if (LONG_MAX == mImpl->seconds) { + if (LONG_MAX == secs) { return LONG_MAX; } - return mImpl->seconds * 1000; + return secs * 1000; } long TimeInterval::seconds() const noexcept { - return mImpl->seconds; + return secs; } TimeInterval::operator std::string() const noexcept { - if (mImpl->seconds == LONG_MAX) { + if (secs == LONG_MAX) { return "never"; } - return std::to_string(mImpl->seconds); + return std::to_string(secs); } diff --git a/herald/src/datatype/uuid.cpp b/herald/src/datatype/uuid.cpp index 73748b2..c918f2a 100644 --- a/herald/src/datatype/uuid.cpp +++ b/herald/src/datatype/uuid.cpp @@ -16,26 +16,26 @@ namespace herald { namespace datatype { // PIMPL Class -class UUID::Impl { -public: - using value_type = uint8_t; +// class UUID::Impl { +// public: +// using value_type = uint8_t; - Impl(std::array& data, bool isValid); - ~Impl(); +// Impl(std::array& data, bool isValid); +// ~Impl(); - std::array mData = { {0}}; - bool mValid; -}; +// std::array mData = { {0}}; +// bool mValid; +// }; -UUID::Impl::Impl(std::array& data, bool isValid) - : mValid(isValid) -{ - mData = std::move(data); -} +// UUID::Impl::Impl(std::array& data, bool isValid) +// : mValid(isValid) +// { +// mData = std::move(data); +// } -UUID::Impl::~Impl() { - ; -} +// UUID::Impl::~Impl() { +// ; +// } @@ -69,64 +69,62 @@ UUID::random(RandomnessGenerator& from) noexcept { // Instance functions UUID::UUID(UUID&& from) - : mImpl(std::make_unique(from.mImpl->mData,from.mImpl->mValid)) + : mData(std::move(from.mData)), mValid(from.mValid) { ; } UUID::UUID(const UUID& from) - : mImpl(std::make_unique(from.mImpl->mData,from.mImpl->mValid)) + : mData(from.mData),mValid(from.mValid) { ; } // private ctor UUID::UUID(std::array data, bool isValid) noexcept - : mImpl(std::make_unique(data,isValid)) + : mData(data),mValid(isValid) { ; } -UUID::~UUID() { - ; -} +UUID::~UUID() = default; UUID& UUID::operator=(const UUID& other) noexcept { - mImpl->mData = other.mImpl->mData; - mImpl->mValid = other.mImpl->mValid; + mData = other.mData; + mValid = other.mValid; return *this; } bool UUID::valid() const noexcept { - return mImpl->mValid; + return mValid; } bool UUID::operator==(const UUID& other) const noexcept { - return mImpl->mData == other.mImpl->mData; + return mData == other.mData; } bool UUID::operator!=(const UUID& other) const noexcept { - return mImpl->mData != other.mImpl->mData; + return mData != other.mData; } bool UUID::operator<(const UUID& other) const noexcept { - return mImpl->mData < other.mImpl->mData; + return mData < other.mData; } bool UUID::operator>(const UUID& other) const noexcept { - return mImpl->mData > other.mImpl->mData; + return mData > other.mData; } std::array UUID::data() const noexcept { - return mImpl->mData; + return mData; } std::string @@ -136,7 +134,7 @@ UUID::string() const noexcept { str.setf(std::ios_base::hex, std::ios::basefield); str.fill('0'); for (std::size_t i=0; i < 16; i++) { - str << std::setw(2) << (unsigned short)mImpl->mData[i]; + str << std::setw(2) << (unsigned short)mData[i]; } std::string hexString = str.str(); // add in hyphens at relevant points diff --git a/herald/src/default_sensor_delegate.cpp b/herald/src/default_sensor_delegate.cpp index 2d08f5a..081eeba 100644 --- a/herald/src/default_sensor_delegate.cpp +++ b/herald/src/default_sensor_delegate.cpp @@ -28,8 +28,9 @@ void DefaultSensorDelegate::sensor(SensorType sensor, const Proximity& didMeasure, const TargetIdentifier& fromTarget) { }; +template void -DefaultSensorDelegate::sensor(SensorType sensor, const Location& didVisit) { +DefaultSensorDelegate::sensor(SensorType sensor, const Location& didVisit) { }; void From 2719520ba3b20a9069bbb5a1c8a52d78bd11a3aa Mon Sep 17 00:00:00 2001 From: Adam Fowler Date: Sat, 20 Mar 2021 21:05:05 +0000 Subject: [PATCH 10/28] Related to #24. Reduced binary and SRAM size Modifications for this commit in Zephyr build options affecting memory. Known issue: Wearable has include issue on anything but nRF5340dk. Issue reported to Nordic: https://devzone.nordicsemi.com/f/nordic-q-a/73046/undefined-reference-to-k_aligned_alloc-in-kernel-h-via-gatt_dm-c Signed-off-by: Adam Fowler --- config/zephyr/debug.conf | 2 +- config/zephyr/nRF52832.conf | 16 ++++++++-------- config/zephyr/nRF52833.conf | 16 ++++++++-------- config/zephyr/nRF52840.conf | 16 ++++++++-------- config/zephyr/nRF5340.conf | 15 ++++++++------- herald-wearable/CMakeLists.txt | 4 ++-- herald-wearable/Kconfig | 7 ------- herald-wearable/src/main.cpp | 3 ++- make-all-zephyr.sh | 16 ++++++++-------- 9 files changed, 45 insertions(+), 50 deletions(-) delete mode 100644 herald-wearable/Kconfig diff --git a/config/zephyr/debug.conf b/config/zephyr/debug.conf index 66e6510..679d1a3 100644 --- a/config/zephyr/debug.conf +++ b/config/zephyr/debug.conf @@ -6,7 +6,7 @@ CONFIG_GPIO=y CONFIG_UART_CONSOLE=n # Debug log settings -CONFIG_LOG_STRDUP_BUF_COUNT=500 +CONFIG_LOG_STRDUP_BUF_COUNT=50 CONFIG_LOG_STRDUP_MAX_STRING=160 CONFIG_LOG_DEFAULT_LEVEL=3 diff --git a/config/zephyr/nRF52832.conf b/config/zephyr/nRF52832.conf index 3d2447b..d27b69d 100644 --- a/config/zephyr/nRF52832.conf +++ b/config/zephyr/nRF52832.conf @@ -4,15 +4,15 @@ CONFIG_ENTROPY_GENERATOR=y # Simple Payload support BEGINS # Option 1: MBEDTLS -CONFIG_MBEDTLS_VANILLA_BACKEND=y -CONFIG_MBEDTLS_MAC_SHA256_ENABLED=y -CONFIG_MBEDTLS=y -CONFIG_MBEDTLS_BUILTIN=y -CONFIG_MBEDTLS_CFG_FILE="config-tls-generic.h" +#CONFIG_MBEDTLS_VANILLA_BACKEND=y +#CONFIG_MBEDTLS_MAC_SHA256_ENABLED=y +#CONFIG_MBEDTLS=y +#CONFIG_MBEDTLS_BUILTIN=y +#CONFIG_MBEDTLS_CFG_FILE="config-tls-generic.h" # Option 2: TINYCRYPT -# CONFIG_TINYCRYPT_SHA256=y +CONFIG_TINYCRYPT_SHA256=y # Simple Payload Support ENDS -CONFIG_BT_MAX_CONN=20 -# VERIFY above for this machine \ No newline at end of file +#CONFIG_BT_MAX_CONN=20 +CONFIG_BT_MAX_CONN=5 \ No newline at end of file diff --git a/config/zephyr/nRF52833.conf b/config/zephyr/nRF52833.conf index 3d2447b..5ec3548 100644 --- a/config/zephyr/nRF52833.conf +++ b/config/zephyr/nRF52833.conf @@ -4,15 +4,15 @@ CONFIG_ENTROPY_GENERATOR=y # Simple Payload support BEGINS # Option 1: MBEDTLS -CONFIG_MBEDTLS_VANILLA_BACKEND=y -CONFIG_MBEDTLS_MAC_SHA256_ENABLED=y -CONFIG_MBEDTLS=y -CONFIG_MBEDTLS_BUILTIN=y -CONFIG_MBEDTLS_CFG_FILE="config-tls-generic.h" +#CONFIG_MBEDTLS_VANILLA_BACKEND=y +#CONFIG_MBEDTLS_MAC_SHA256_ENABLED=y +#CONFIG_MBEDTLS=y +#CONFIG_MBEDTLS_BUILTIN=y +#CONFIG_MBEDTLS_CFG_FILE="config-tls-generic.h" # Option 2: TINYCRYPT -# CONFIG_TINYCRYPT_SHA256=y +CONFIG_TINYCRYPT_SHA256=y # Simple Payload Support ENDS -CONFIG_BT_MAX_CONN=20 -# VERIFY above for this machine \ No newline at end of file +#CONFIG_BT_MAX_CONN=20 +CONFIG_BT_MAX_CONN=10 \ No newline at end of file diff --git a/config/zephyr/nRF52840.conf b/config/zephyr/nRF52840.conf index 1787671..b7c190d 100644 --- a/config/zephyr/nRF52840.conf +++ b/config/zephyr/nRF52840.conf @@ -9,15 +9,15 @@ CONFIG_ENTROPY_GENERATOR=y # Simple Payload support BEGINS # Option 1: MBEDTLS -CONFIG_MBEDTLS_VANILLA_BACKEND=y -CONFIG_MBEDTLS_MAC_SHA256_ENABLED=y -CONFIG_MBEDTLS=y -CONFIG_MBEDTLS_BUILTIN=y -CONFIG_MBEDTLS_CFG_FILE="config-tls-generic.h" +#CONFIG_MBEDTLS_VANILLA_BACKEND=y +#CONFIG_MBEDTLS_MAC_SHA256_ENABLED=y +#CONFIG_MBEDTLS=y +#CONFIG_MBEDTLS_BUILTIN=y +#CONFIG_MBEDTLS_CFG_FILE="config-tls-generic.h" # Option 2: TINYCRYPT -# CONFIG_TINYCRYPT_SHA256=y +CONFIG_TINYCRYPT_SHA256=y # Simple Payload Support ENDS -CONFIG_BT_MAX_CONN=20 -# VERIFY above for this machine \ No newline at end of file +#CONFIG_BT_MAX_CONN=20 +CONFIG_BT_MAX_CONN=10 \ No newline at end of file diff --git a/config/zephyr/nRF5340.conf b/config/zephyr/nRF5340.conf index c7ffbe3..814375d 100644 --- a/config/zephyr/nRF5340.conf +++ b/config/zephyr/nRF5340.conf @@ -9,14 +9,15 @@ CONFIG_ENTROPY_GENERATOR=y # Simple Payload support BEGINS # Option 1: MBEDTLS -CONFIG_MBEDTLS_VANILLA_BACKEND=y -CONFIG_MBEDTLS_MAC_SHA256_ENABLED=y -CONFIG_MBEDTLS=y -CONFIG_MBEDTLS_BUILTIN=y -CONFIG_MBEDTLS_CFG_FILE="config-tls-generic.h" +#CONFIG_MBEDTLS_VANILLA_BACKEND=y +#CONFIG_MBEDTLS_MAC_SHA256_ENABLED=y +#CONFIG_MBEDTLS=y +#CONFIG_MBEDTLS_BUILTIN=y +#CONFIG_MBEDTLS_CFG_FILE="config-tls-generic.h" # Option 2: TINYCRYPT -# CONFIG_TINYCRYPT_SHA256=y +CONFIG_TINYCRYPT_SHA256=y # Simple Payload Support ENDS -CONFIG_BT_MAX_CONN=64 \ No newline at end of file +#CONFIG_BT_MAX_CONN=64 +CONFIG_BT_MAX_CONN=10 \ No newline at end of file diff --git a/herald-wearable/CMakeLists.txt b/herald-wearable/CMakeLists.txt index 912ffff..f17ce5f 100644 --- a/herald-wearable/CMakeLists.txt +++ b/herald-wearable/CMakeLists.txt @@ -101,7 +101,7 @@ add_compile_options(--no-undefined -ffunction-sections -fdata-sections -s -fno-e target_sources(app PRIVATE ${HERALD_SOURCES} ${HERALD_SOURCES_ZEPHYR} - #${HERALD_SOURCES_TINYCRYPT} - ${HERALD_SOURCES_MBEDTLS} + ${HERALD_SOURCES_TINYCRYPT} + #${HERALD_SOURCES_MBEDTLS} src/main.cpp ) diff --git a/herald-wearable/Kconfig b/herald-wearable/Kconfig deleted file mode 100644 index c89db54..0000000 --- a/herald-wearable/Kconfig +++ /dev/null @@ -1,7 +0,0 @@ -# Copyright (c) 2019 Nordic Semiconductor ASA -# SPDX-License-Identifier: Apache-2.0 - -config USB_DEVICE_PID - default USB_PID_CONSOLE_SAMPLE - -source "Kconfig.zephyr" diff --git a/herald-wearable/src/main.cpp b/herald-wearable/src/main.cpp index 5ae3863..c8487de 100644 --- a/herald-wearable/src/main.cpp +++ b/herald-wearable/src/main.cpp @@ -112,7 +112,8 @@ class AppLoggingDelegate : public herald::SensorDelegate { } /// Detection of time spent at location, e.g. at specific restaurant between 02/06/2020 19:00 and 02/06/2020 21:00 - void sensor(SensorType sensor, const Location& didVisit) override { + template + void sensor(SensorType sensor, const Location& didVisit) { LOG_DBG("sensor didVisit"); } diff --git a/make-all-zephyr.sh b/make-all-zephyr.sh index 81c6733..d15d3df 100644 --- a/make-all-zephyr.sh +++ b/make-all-zephyr.sh @@ -14,25 +14,25 @@ mkdir build-52833 cd build-5340 export BOARD=nrf5340dk_nrf5340_cpuapp -cmake .. +cmake -DCMAKE_BUILD_TYPE=Release .. make -j 32 cd .. cd build-52840 export BOARD=nrf52840dongle_nrf52840 -cmake .. +cmake -DCMAKE_BUILD_TYPE=Release .. make -j 32 cd .. cd build-52832 export BOARD=nrf52dk_nrf52832 -cmake .. +cmake -DCMAKE_BUILD_TYPE=Release .. make -j 32 cd .. cd build-52833 export BOARD=nrf52833dk_nrf52833 -cmake .. +cmake -DCMAKE_BUILD_TYPE=Release .. make -j 32 cd .. @@ -46,25 +46,25 @@ mkdir build-52833 cd build-5340 export BOARD=nrf5340dk_nrf5340_cpuapp -cmake .. +cmake -DCMAKE_BUILD_TYPE=Release .. make -j 32 cd .. cd build-52840 export BOARD=nrf52840dongle_nrf52840 -cmake .. +cmake -DCMAKE_BUILD_TYPE=Release .. make -j 32 cd .. cd build-52832 export BOARD=nrf52dk_nrf52832 -cmake .. +cmake -DCMAKE_BUILD_TYPE=Release .. make -j 32 cd .. cd build-52833 export BOARD=nrf52833dk_nrf52833 -cmake .. +cmake -DCMAKE_BUILD_TYPE=Release .. make -j 32 cd .. From afbec6f6bda3f854ea68eca7556f39b6431a4f8d Mon Sep 17 00:00:00 2001 From: Adam Fowler Date: Sun, 21 Mar 2021 01:33:11 +0000 Subject: [PATCH 11/28] Related to #24. Builds for both apps now succeeding for all 4 boards Signed-off-by: Adam Fowler --- config/zephyr/receiver.conf | 5 ++++- make-all-zephyr.sh | 1 + 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/config/zephyr/receiver.conf b/config/zephyr/receiver.conf index 18771bf..df892b8 100644 --- a/config/zephyr/receiver.conf +++ b/config/zephyr/receiver.conf @@ -6,4 +6,7 @@ CONFIG_BT_GATT_CLIENT=y CONFIG_BT_GATT_DM=y # Scanning -CONFIG_BT_SCAN=y \ No newline at end of file +CONFIG_BT_SCAN=y + +# Fix for use of heap in gatt_gm erroring with undefined reference to k_aligned_alloc +CONFIG_HEAP_MEM_POOL_SIZE=256 \ No newline at end of file diff --git a/make-all-zephyr.sh b/make-all-zephyr.sh index d15d3df..b6c3a2a 100644 --- a/make-all-zephyr.sh +++ b/make-all-zephyr.sh @@ -94,6 +94,7 @@ cp ../herald-venue-beacon/build-5340/hci_rpmsg/zephyr/zephyr.hex nrf5340dk/heral cp ../herald-venue-beacon/build-52840/zephyr/zephyr.hex nrf52840dk/herald-venue-beacon/ cp ../herald-venue-beacon/build-52832/zephyr/zephyr.hex nrf52832dk/herald-venue-beacon/ cp ../herald-venue-beacon/build-52833/zephyr/zephyr.hex nrf52833dk/herald-venue-beacon/ +ls -la nrf*/*/*.hex tar cjf zephyr-binaries.tar.gz nrf* cd .. From bd9cc3b64eab3a103a5ca5fddede564dc7946979 Mon Sep 17 00:00:00 2001 From: Adam Fowler Date: Sun, 21 Mar 2021 02:03:43 +0000 Subject: [PATCH 12/28] SensorDelegate RSSI source created and passes tests. Signed-off-by: Adam Fowler --- herald-tests/CMakeLists.txt | 1 + herald-tests/analysissensor-tests.cpp | 104 ++++++++++++++++++ herald/herald.cmake | 1 + herald/include/herald.h | 1 + .../include/herald/analysis/sensor_source.h | 36 ++++++ 5 files changed, 143 insertions(+) create mode 100644 herald-tests/analysissensor-tests.cpp create mode 100644 herald/include/herald/analysis/sensor_source.h diff --git a/herald-tests/CMakeLists.txt b/herald-tests/CMakeLists.txt index b0d5c41..49fc8f3 100644 --- a/herald-tests/CMakeLists.txt +++ b/herald-tests/CMakeLists.txt @@ -20,6 +20,7 @@ add_executable(herald-tests sample-tests.cpp ranges-tests.cpp analysisrunner-tests.cpp + analysissensor-tests.cpp # high level advertparser-tests.cpp diff --git a/herald-tests/analysissensor-tests.cpp b/herald-tests/analysissensor-tests.cpp new file mode 100644 index 0000000..bf479ea --- /dev/null +++ b/herald-tests/analysissensor-tests.cpp @@ -0,0 +1,104 @@ +// Copyright 2021 Herald project contributors +// SPDX-License-Identifier: Apache-2.0 +// + +#include "catch.hpp" + +#include "herald/herald.h" + +#include +#include + +using namespace herald; +using namespace herald::analysis::sampling; +using namespace herald::datatype; + +struct DummyDistanceDelegate { + using value_type = Distance; + + DummyDistanceDelegate() : lastSampledID(0), distances() {}; + DummyDistanceDelegate(const DummyDistanceDelegate&) = delete; // copy ctor deleted + DummyDistanceDelegate(DummyDistanceDelegate&& other) noexcept : lastSampledID(other.lastSampledID), distances(std::move(other.distances)) {} // move ctor + ~DummyDistanceDelegate() {}; + + DummyDistanceDelegate& operator=(DummyDistanceDelegate&& other) noexcept { + lastSampledID = other.lastSampledID; + std::swap(distances,other.distances); + return *this; + } + + // specific override of template + void newSample(SampledID sampled, Sample sample) { + lastSampledID = sampled; + distances.push(sample); + } + + void reset() { + distances.clear(); + lastSampledID = 0; + } + + // Test only methods + SampledID lastSampled() { + return lastSampledID; + } + + const SampleList,25>& samples() { + return distances; + } + +private: + SampledID lastSampledID; + SampleList,25> distances; +}; + + +/// [Who] As a DCT app developer +/// [What] I want to link my live application data to an analysis runner easily +/// [Value] So I don't have to write plumbing code for Herald itself +/// +/// [Who] As a DCT app developer +/// [What] I want to periodically run analysis aggregates automatically +/// [Value] So I don't miss any information, and have accurate, regular, samples +TEST_CASE("analysissensor-rssi-basic", "[analysissensor][rssi][basic]") { + SECTION("analysissensor-rssi-basic") { + Proximity p1{.unit = ProximityMeasurementUnit::RSSI, .value = -55}; + Proximity p2{.unit = ProximityMeasurementUnit::RSSI, .value = -56}; + Proximity p3{.unit = ProximityMeasurementUnit::RSSI, .value = -57}; + Proximity p4{.unit = ProximityMeasurementUnit::RSSI, .value = -58}; + + herald::analysis::algorithms::distance::FowlerBasicAnalyser distanceAnalyser(0, -50, -24); // 0 = run every time run() is called + + DummyDistanceDelegate myDelegate; + herald::analysis::AnalysisDelegateManager adm(std::move(myDelegate)); // NOTE: myDelegate MOVED FROM and no longer accessible + herald::analysis::AnalysisProviderManager apm(std::move(distanceAnalyser)); // NOTE: distanceAnalyser MOVED FROM and no longer accessible + + herald::analysis::AnalysisRunner< + herald::analysis::AnalysisDelegateManager, + herald::analysis::AnalysisProviderManager, + RSSI,Distance + > runner(adm, apm); // just for Sample types, and their produced output (Sample) + + std::shared_ptr> src = std::make_shared>(runner); + PayloadData payload(std::byte(5),4); + TargetIdentifier id(Data(std::byte(3),16)); + src->sensor(SensorType::BLE, p1, id, payload); + src->sensor(SensorType::BLE, p2, id, payload); + runner.run(Date()); // In an app, use a Coordinator task + src->sensor(SensorType::BLE, p3, id, payload); + src->sensor(SensorType::BLE, p4, id, payload); + runner.run(Date()); // In an app, use a Coordinator task + + auto& delegateRef = adm.get(); + REQUIRE(delegateRef.lastSampled() != 0); + + auto& samples = delegateRef.samples(); + REQUIRE(samples.size() == 1); // Only 1 because time will run in 'real time' as its a sensor source (dynamic date) + REQUIRE(samples[0].taken.secondsSinceUnixEpoch() != 0); + REQUIRE(samples[0].value != 0.0); + + // Let's see the total memory in use... + std::cout << "AnalysisRunner::RAM = " << sizeof(runner) << std::endl; + std::cout << "SensorDelegateRSSISource::RAM = " << sizeof(*src) << std::endl; + } +} \ No newline at end of file diff --git a/herald/herald.cmake b/herald/herald.cmake index 4288e72..3aca6f3 100644 --- a/herald/herald.cmake +++ b/herald/herald.cmake @@ -15,6 +15,7 @@ set(HERALD_HEADERS ${HERALD_BASE}/include/herald/analysis/ranges.h ${HERALD_BASE}/include/herald/analysis/risk.h ${HERALD_BASE}/include/herald/analysis/sampling.h + ${HERALD_BASE}/include/herald/analysis/sensor_source.h ${HERALD_BASE}/include/herald/ble/ble_concrete.h ${HERALD_BASE}/include/herald/ble/ble_coordinator.h ${HERALD_BASE}/include/herald/ble/ble_database_delegate.h diff --git a/herald/include/herald.h b/herald/include/herald.h index e979db0..307aeff 100644 --- a/herald/include/herald.h +++ b/herald/include/herald.h @@ -86,6 +86,7 @@ #include "herald/analysis/ranges.h" #include "herald/analysis/risk.h" #include "herald/analysis/sampling.h" +#include "herald/analysis/sensor_source.h" // payload namespace #include "herald/payload/payload_data_supplier.h" diff --git a/herald/include/herald/analysis/sensor_source.h b/herald/include/herald/analysis/sensor_source.h new file mode 100644 index 0000000..7c9c0f3 --- /dev/null +++ b/herald/include/herald/analysis/sensor_source.h @@ -0,0 +1,36 @@ +// Copyright 2021 Herald Project Contributors +// SPDX-License-Identifier: Apache-2.0 +// + +#ifndef HERALD_ANALYSIS_SENSOR_SOURCE_H +#define HERALD_ANALYSIS_SENSOR_SOURCE_H + +#include "sampling.h" +#include "../default_sensor_delegate.h" +#include "../datatype/rssi.h" + +namespace herald { +namespace analysis { + +using namespace sampling; + +/// \brief Connects the RSSI readings from a SensorDelegate to a source for AnalysisRunner data +template +struct SensorDelegateRSSISource : DefaultSensorDelegate { + + SensorDelegateRSSISource(RunnerT& runner) : runner(runner) {}; + ~SensorDelegateRSSISource() = default; + + void sensor(SensorType sensor, const Proximity& didMeasure, const TargetIdentifier& fromTarget, const PayloadData& withPayload) override { + if (sensor != SensorType::BLE) return; // guard for BLE RSSI proximity only data + runner.template newSample(withPayload.hashCode(),Sample(Date(),RSSI(didMeasure.value))); + } + +private: + RunnerT& runner; // reference to app wide Analysis Runner instance +}; + +} +} + +#endif From 274fff69cb78e2bee023604506ef8c1dc16154c1 Mon Sep 17 00:00:00 2001 From: Adam Fowler Date: Sun, 21 Mar 2021 11:29:47 +0000 Subject: [PATCH 13/28] Part of #42. Analysis logging tests passing Known issue: Still experiencing stack overflows in wearable example. Signed-off-by: Adam Fowler --- config/zephyr/nRF5340.conf | 6 +- config/zephyr/receiver.conf | 5 +- herald-tests-zephyr/CMakeLists.txt | 39 +++--- herald-tests/analysissensor-tests.cpp | 87 ++++++++++++ herald-wearable/CMakeLists.txt | 2 +- herald-wearable/prj.conf | 4 + herald-wearable/src/main.cpp | 129 ++++++++++-------- herald/herald.cmake | 3 +- herald/include/herald.h | 3 +- .../analysis/logging_analysis_delegate.h | 68 +++++++++ 10 files changed, 267 insertions(+), 79 deletions(-) create mode 100644 herald/include/herald/analysis/logging_analysis_delegate.h diff --git a/config/zephyr/nRF5340.conf b/config/zephyr/nRF5340.conf index 814375d..5edf953 100644 --- a/config/zephyr/nRF5340.conf +++ b/config/zephyr/nRF5340.conf @@ -20,4 +20,8 @@ CONFIG_TINYCRYPT_SHA256=y # Simple Payload Support ENDS #CONFIG_BT_MAX_CONN=64 -CONFIG_BT_MAX_CONN=10 \ No newline at end of file +CONFIG_BT_MAX_CONN=10 + +# Requirement of ncs/v1.5.0/zephyr/drivers/bluetooth/hci/rpmsg_nrf53.c + +CONFIG_HEAP_MEM_POOL_SIZE=1024 \ No newline at end of file diff --git a/config/zephyr/receiver.conf b/config/zephyr/receiver.conf index df892b8..4938f1a 100644 --- a/config/zephyr/receiver.conf +++ b/config/zephyr/receiver.conf @@ -9,4 +9,7 @@ CONFIG_BT_GATT_DM=y CONFIG_BT_SCAN=y # Fix for use of heap in gatt_gm erroring with undefined reference to k_aligned_alloc -CONFIG_HEAP_MEM_POOL_SIZE=256 \ No newline at end of file +CONFIG_HEAP_MEM_POOL_SIZE=256 + +# Issue #50 to prevent stack overflow in CPU +CONFIG_BT_RX_STACK_SIZE=8192 diff --git a/herald-tests-zephyr/CMakeLists.txt b/herald-tests-zephyr/CMakeLists.txt index c5b2ec4..352cbeb 100644 --- a/herald-tests-zephyr/CMakeLists.txt +++ b/herald-tests-zephyr/CMakeLists.txt @@ -100,35 +100,38 @@ target_sources(app PRIVATE ${HERALD_SOURCES_ZEPHYR} # Simple Payload - Choose ONE # Option 1: MBED TLS - ${HERALD_SOURCES_MBEDTLS} + #${HERALD_SOURCES_MBEDTLS} # Option 2: TinyCrypt - # ${HERALD_SOURCES_TINYCRYPT} + ${HERALD_SOURCES_TINYCRYPT} # Now tests source # root with main - ../herald-tests/datatypes-tests.cpp + #../herald-tests/datatypes-tests.cpp + # ^^^ FIX THIS FROM DYING ALL THE TIME -> MAY NEED TO REWRITE DATA CLASS - SEE STL STRING TRICK # Low level # ../herald-tests/sensorlogger-tests.cpp # Don't run this on Zephyr - ../herald-tests/errorcontactlog-tests.cpp - ../herald-tests/data-tests.cpp - ../herald-tests/blemacaddress-tests.cpp - ../herald-tests/targetidentifier-tests.cpp + #../herald-tests/errorcontactlog-tests.cpp + #../herald-tests/data-tests.cpp + #../herald-tests/blemacaddress-tests.cpp + #../herald-tests/targetidentifier-tests.cpp # mid level - ../herald-tests/beaconpayload-tests.cpp - ../herald-tests/extendeddata-tests.cpp - ../herald-tests/fixedpayload-tests.cpp - ../herald-tests/simplepayload-tests.cpp - ../herald-tests/bledevice-tests.cpp - ../herald-tests/sample-tests.cpp - ../herald-tests/ranges-tests.cpp + ../herald-tests/analysisrunner-tests.cpp + #../herald-tests/analysissensor-tests.cpp + #../herald-tests/beaconpayload-tests.cpp + #../herald-tests/extendeddata-tests.cpp + #../herald-tests/fixedpayload-tests.cpp + #../herald-tests/simplepayload-tests.cpp + #../herald-tests/bledevice-tests.cpp + #../herald-tests/sample-tests.cpp + #../herald-tests/ranges-tests.cpp # high level - ../herald-tests/advertparser-tests.cpp - ../herald-tests/bledatabase-tests.cpp - ../herald-tests/blecoordinator-tests.cpp - ../herald-tests/coordinator-tests.cpp + #../herald-tests/advertparser-tests.cpp + #../herald-tests/bledatabase-tests.cpp + #../herald-tests/blecoordinator-tests.cpp + #../herald-tests/coordinator-tests.cpp # now main func src/main.cpp diff --git a/herald-tests/analysissensor-tests.cpp b/herald-tests/analysissensor-tests.cpp index bf479ea..c6ebca6 100644 --- a/herald-tests/analysissensor-tests.cpp +++ b/herald-tests/analysissensor-tests.cpp @@ -52,6 +52,36 @@ struct DummyDistanceDelegate { SampleList,25> distances; }; +class DummyLogger : public herald::data::SensorLoggingSink { +public: + DummyLogger(std::string sub,std::string cat) : subsystem(sub), category(cat), value() {} + ~DummyLogger() = default; + + void log(herald::data::SensorLoggerLevel level, std::string message) override { + value = subsystem + "," + category + "," + message; + } + + std::string subsystem; + std::string category; + std::string value; +}; + +class DummyContext : public herald::Context { +public: + DummyContext() = default; + ~DummyContext() = default; + + std::shared_ptr getBluetoothStateManager() override { return nullptr;} + + std::shared_ptr getLoggingSink(const std::string& subsystemFor, + const std::string& categoryFor) override + { + lastLogger = std::make_shared(subsystemFor,categoryFor); + return lastLogger; + } + + std::shared_ptr lastLogger; +}; /// [Who] As a DCT app developer /// [What] I want to link my live application data to an analysis runner easily @@ -100,5 +130,62 @@ TEST_CASE("analysissensor-rssi-basic", "[analysissensor][rssi][basic]") { // Let's see the total memory in use... std::cout << "AnalysisRunner::RAM = " << sizeof(runner) << std::endl; std::cout << "SensorDelegateRSSISource::RAM = " << sizeof(*src) << std::endl; + } +} + + + +TEST_CASE("analysissensor-output", "[sensorlogger][analysissensor][output]") { + SECTION("analysissensor-output") { + std::shared_ptr ctx = std::make_shared(); + //herald::data::SensorLogger logger(ctx,"testout","analysissensor"); + herald::analysis::LoggingAnalysisDelegate lad(ctx); // The subject of this test + std::cout << "LoggingAnalysisDelegate::RAM = " << sizeof(lad) << std::endl; + + + Proximity p1{.unit = ProximityMeasurementUnit::RSSI, .value = -55}; + Proximity p2{.unit = ProximityMeasurementUnit::RSSI, .value = -56}; + Proximity p3{.unit = ProximityMeasurementUnit::RSSI, .value = -57}; + Proximity p4{.unit = ProximityMeasurementUnit::RSSI, .value = -58}; + + herald::analysis::algorithms::distance::FowlerBasicAnalyser distanceAnalyser(0, -50, -24); // 0 = run every time run() is called + + DummyDistanceDelegate myDelegate; + herald::analysis::AnalysisDelegateManager adm(std::move(myDelegate), std::move(lad)); // NOTE: myDelegate MOVED FROM and no longer accessible + herald::analysis::AnalysisProviderManager apm(std::move(distanceAnalyser)); // NOTE: distanceAnalyser MOVED FROM and no longer accessible + + herald::analysis::AnalysisRunner< + herald::analysis::AnalysisDelegateManager>, + herald::analysis::AnalysisProviderManager, + RSSI,Distance + > runner(adm, apm); // just for Sample types, and their produced output (Sample) + + std::shared_ptr> src = std::make_shared>(runner); + PayloadData payload(std::byte(5),4); + TargetIdentifier id(Data(std::byte(3),16)); + src->sensor(SensorType::BLE, p1, id, payload); + src->sensor(SensorType::BLE, p2, id, payload); + runner.run(Date()); // In an app, use a Coordinator task + src->sensor(SensorType::BLE, p3, id, payload); + src->sensor(SensorType::BLE, p4, id, payload); + runner.run(Date()); // In an app, use a Coordinator task + + auto& delegateRef = adm.get(); + REQUIRE(delegateRef.lastSampled() != 0); + + auto& samples = delegateRef.samples(); + REQUIRE(samples.size() == 1); // Only 1 because time will run in 'real time' as its a sensor source (dynamic date) + REQUIRE(samples[0].taken.secondsSinceUnixEpoch() != 0); + REQUIRE(samples[0].value != 0.0); + + auto lastMsg = ctx->lastLogger->value; + REQUIRE(lastMsg != ""); // must have logged something... + std::cout << "Last log message: " << lastMsg << std::endl; + + // Let's see the total memory in use... + std::cout << "AnalysisRunner::RAM = " << sizeof(runner) << std::endl; + std::cout << "SensorDelegateRSSISource::RAM = " << sizeof(src.get()) << std::endl; + + } } \ No newline at end of file diff --git a/herald-wearable/CMakeLists.txt b/herald-wearable/CMakeLists.txt index f17ce5f..3c60ca2 100644 --- a/herald-wearable/CMakeLists.txt +++ b/herald-wearable/CMakeLists.txt @@ -96,7 +96,7 @@ endif() #HERALD_LOG_LEVEL 4 = debug, 3 = info, 2 = warn / errors (Zephyr LOG_WARN), 1 = info contacts log only (Zephyr LOG_ERR) # gcc arm does not warn about missing defs until runtime -add_compile_options(--no-undefined -ffunction-sections -fdata-sections -s -fno-exception -fno-rtti -Wl,-z,defs,norelro,--strict,--strict_symbols,--gc-sections,--hash-style=gnu) +add_compile_options(--no-undefined -ffunction-sections -fdata-sections -s -fexceptions -fno-rtti -Wl,-z,defs,norelro,--strict,--strict_symbols,--gc-sections,--hash-style=gnu) target_sources(app PRIVATE ${HERALD_SOURCES} diff --git a/herald-wearable/prj.conf b/herald-wearable/prj.conf index 1bb225b..bf3c47c 100644 --- a/herald-wearable/prj.conf +++ b/herald-wearable/prj.conf @@ -1,2 +1,6 @@ # Wearable specific settings (see config/zephyr/base.conf et al for mixed-in settings) CONFIG_BT_DEVICE_NAME="Herald Wearable" + +# Analysis engine uses variant, and so can have (an extremely hard to create) invalid variant exception +CONFIG_EXCEPTIONS=y +CONFIG_HW_STACK_PROTECTION=y diff --git a/herald-wearable/src/main.cpp b/herald-wearable/src/main.cpp index c8487de..038c88a 100644 --- a/herald-wearable/src/main.cpp +++ b/herald-wearable/src/main.cpp @@ -191,68 +191,68 @@ void herald_entry() { // TESTING ONLY // IF IN TESTING / DEBUG, USE A FIXED PAYLOAD (SO YOU CAN TRACK IT OVER TIME) - // std::uint64_t clientId = 1234567890; // TODO generate unique device ID from device hardware info (for static, test only, payload) - // std::uint8_t uniqueId[8]; - // // 7. Implement a consistent post restart valid ID from a hardware identifier (E.g. nRF serial number) - // auto hwInfoAvailable = hwinfo_get_device_id(uniqueId,sizeof(uniqueId)); - // if (hwInfoAvailable > 0) { - // LOG_DBG("Read %d bytes for a unique, persistent, device ID", hwInfoAvailable); - // clientId = *uniqueId; - // } else { - // LOG_DBG("Couldn't read hardware info for zephyr device. Error code: %d", hwInfoAvailable); - // } - // LOG_DBG("Final clientID: %d", clientId); - - // std::shared_ptr pds = std::make_shared( - // countryCode, - // stateCode, - // clientId - // ); - // END TESTING ONLY - - // PRODUCTION ONLY - LOG_DBG("Before simple"); - k_sleep(K_SECONDS(2)); - // Use the simple payload, or secured payload, that implements privacy features to prevent user tracking - herald::payload::simple::K k; - // NOTE: You should store a secret key for a period of days and pass the value for the correct epoch in to here instead of sk - - // Note: Using the CC310 to do this. You can use RandomnessSource.h random sources instead if you wish, but CC310 is more secure. - herald::payload::simple::SecretKey sk(std::byte(0x00),2048); // fallback - you should do something different. - - size_t buflen = 2048; - uint8_t* buf = new uint8_t[buflen]; - size_t olen = 0; - -#ifdef CC3XX_BACKEND - int success = nrf_cc3xx_platform_entropy_get(buf,buflen,&olen); -#else - int success = 1; -#endif - if (0 == success) { - sk.clear(); - sk.append(buf, 0, buflen); - LOG_DBG("Have applied CC3xx generated data to secret key"); + std::uint64_t clientId = 1234567890; // TODO generate unique device ID from device hardware info (for static, test only, payload) + std::uint8_t uniqueId[8]; + // 7. Implement a consistent post restart valid ID from a hardware identifier (E.g. nRF serial number) + auto hwInfoAvailable = hwinfo_get_device_id(uniqueId,sizeof(uniqueId)); + if (hwInfoAvailable > 0) { + LOG_DBG("Read %d bytes for a unique, persistent, device ID", hwInfoAvailable); + clientId = *uniqueId; } else { - LOG_DBG("Could not generate 2048 bytes of randomness required for SimplePayload Secret Key. Falling back to fixed generic secret key."); - } - - // verify secret key - for (int i = 0;i < 2048;i+=64) { - Data t = sk.subdata(i,64); - LOG_DBG("Got 64 bytes from secret key from %d",i); + LOG_DBG("Couldn't read hardware info for zephyr device. Error code: %d", hwInfoAvailable); } + LOG_DBG("Final clientID: %d", clientId); - LOG_DBG("About to create Payload data supplier"); - k_sleep(K_SECONDS(2)); - - std::shared_ptr pds = std::make_shared( - ctx, + std::shared_ptr pds = std::make_shared( countryCode, stateCode, - sk, - k + clientId ); + // END TESTING ONLY + + // PRODUCTION ONLY +// LOG_DBG("Before simple"); +// k_sleep(K_SECONDS(2)); +// // Use the simple payload, or secured payload, that implements privacy features to prevent user tracking +// herald::payload::simple::K k; +// // NOTE: You should store a secret key for a period of days and pass the value for the correct epoch in to here instead of sk + +// // Note: Using the CC310 to do this. You can use RandomnessSource.h random sources instead if you wish, but CC310 is more secure. +// herald::payload::simple::SecretKey sk(std::byte(0x00),2048); // fallback - you should do something different. + +// size_t buflen = 2048; +// uint8_t* buf = new uint8_t[buflen]; +// size_t olen = 0; + +// #ifdef CC3XX_BACKEND +// int success = nrf_cc3xx_platform_entropy_get(buf,buflen,&olen); +// #else +// int success = 1; +// #endif +// if (0 == success) { +// sk.clear(); +// sk.append(buf, 0, buflen); +// LOG_DBG("Have applied CC3xx generated data to secret key"); +// } else { +// LOG_DBG("Could not generate 2048 bytes of randomness required for SimplePayload Secret Key. Falling back to fixed generic secret key."); +// } + +// // verify secret key +// for (int i = 0;i < 2048;i+=64) { +// Data t = sk.subdata(i,64); +// LOG_DBG("Got 64 bytes from secret key from %d",i); +// } + +// LOG_DBG("About to create Payload data supplier"); +// k_sleep(K_SECONDS(2)); + +// std::shared_ptr pds = std::make_shared( +// ctx, +// countryCode, +// stateCode, +// sk, +// k +// ); // END PRODUCTION ONLY LOG_DBG("Have created Payload data supplier"); k_sleep(K_SECONDS(2)); @@ -298,6 +298,22 @@ void herald_entry() { // 3. Create and add a Logging sensor delegate to enable testing of discovery + // 4. Now create a live analysis pipeline and enable RSSI to be sent to it for distance estimation + herald::analysis::algorithms::distance::FowlerBasicAnalyser distanceAnalyser(0, -50, -24); // 0 = run every time run() is called + + herald::analysis::LoggingAnalysisDelegate myDelegate(ctx); + herald::analysis::AnalysisDelegateManager adm(std::move(myDelegate)); // NOTE: myDelegate MOVED FROM and no longer accessible + herald::analysis::AnalysisProviderManager apm(std::move(distanceAnalyser)); // NOTE: distanceAnalyser MOVED FROM and no longer accessible + + herald::analysis::AnalysisRunner< + herald::analysis::AnalysisDelegateManager>, + herald::analysis::AnalysisProviderManager, + RSSI,Distance + > runner(adm, apm); // just for Sample types, and their produced output (Sample) + + std::shared_ptr> src = std::make_shared>(runner); + sa->add(src); + LOG_DBG("Starting sensor array"); k_sleep(K_SECONDS(2)); @@ -318,6 +334,7 @@ void herald_entry() { if (0 == iter % (5000 / delay)) { LOG_DBG("herald thread still running. Iteration: %d", iter); + runner.run(Date()); // Note: You may want to do this less or more regularly depending on your requirements } last = now; diff --git a/herald/herald.cmake b/herald/herald.cmake index 3aca6f3..d486816 100644 --- a/herald/herald.cmake +++ b/herald/herald.cmake @@ -10,10 +10,11 @@ set(HERALD_HEADERS ${HERALD_BASE}/include/herald/sensor_delegate.h ${HERALD_BASE}/include/herald/sensor.h ${HERALD_BASE}/include/herald/analysis/aggregates.h - ${HERALD_BASE}/include/herald/analysis/runner.h ${HERALD_BASE}/include/herald/analysis/distance_conversion.h + ${HERALD_BASE}/include/herald/analysis/logging_analysis_delegate.h ${HERALD_BASE}/include/herald/analysis/ranges.h ${HERALD_BASE}/include/herald/analysis/risk.h + ${HERALD_BASE}/include/herald/analysis/runner.h ${HERALD_BASE}/include/herald/analysis/sampling.h ${HERALD_BASE}/include/herald/analysis/sensor_source.h ${HERALD_BASE}/include/herald/ble/ble_concrete.h diff --git a/herald/include/herald.h b/herald/include/herald.h index 307aeff..c496851 100644 --- a/herald/include/herald.h +++ b/herald/include/herald.h @@ -81,10 +81,11 @@ // analysis namespace #include "herald/analysis/aggregates.h" -#include "herald/analysis/runner.h" #include "herald/analysis/distance_conversion.h" +#include "herald/analysis/logging_analysis_delegate.h" #include "herald/analysis/ranges.h" #include "herald/analysis/risk.h" +#include "herald/analysis/runner.h" #include "herald/analysis/sampling.h" #include "herald/analysis/sensor_source.h" diff --git a/herald/include/herald/analysis/logging_analysis_delegate.h b/herald/include/herald/analysis/logging_analysis_delegate.h new file mode 100644 index 0000000..e893493 --- /dev/null +++ b/herald/include/herald/analysis/logging_analysis_delegate.h @@ -0,0 +1,68 @@ +// Copyright 2021 Herald Project Contributors +// SPDX-License-Identifier: Apache-2.0 +// + +#ifndef HERALD_LOGGING_ANALYSIS_DELEGATE_H +#define HERALD_LOGGING_ANALYSIS_DELEGATE_H + +#include "sampling.h" +#include "../data/sensor_logger.h" +#include "../context.h" + +#include + +namespace herald { +namespace analysis { + +using namespace sampling; + +/// \brief Logs any given type of sample to the Herald logging subsystem as a Debug message +template +struct LoggingAnalysisDelegate { + + using value_type = ValT; + + LoggingAnalysisDelegate() + : ctx(nullptr) + HLOGGERINIT(nullptr,"herald","LoggingAnalysisDelegate") + { + } + + LoggingAnalysisDelegate(std::shared_ptr ctx) + : ctx(ctx) + HLOGGERINIT(ctx,"herald","LoggingAnalysisDelegate") + { + } + + LoggingAnalysisDelegate(const LoggingAnalysisDelegate&) = delete; // copy ctor deleted + LoggingAnalysisDelegate(LoggingAnalysisDelegate&& other) noexcept + : ctx(other.ctx) + HLOGGERINIT(ctx,"herald","LoggingAnalysisDelegate") + { + } // move ctor + + ~LoggingAnalysisDelegate() { + } + + LoggingAnalysisDelegate& operator=(LoggingAnalysisDelegate&& other) noexcept { + ctx = other.ctx; + // TODO assign logger too (may have been default initialised by the Analysis engine) + return *this; + } + + // specific override of template + void newSample(SampledID sampled, Sample sample) { + // Log the read distance as debug + HTDBG("New Sample Recorded"); // TODO include the sampled id, sample time, sample value + } + +private: + std::shared_ptr ctx; + HLOGGER; + +}; + +} +} + +#endif \ No newline at end of file From ac72f284723c3a1ab280220c454642f8b59f332d Mon Sep 17 00:00:00 2001 From: Adam Fowler Date: Sun, 21 Mar 2021 16:34:55 +0000 Subject: [PATCH 14/28] Related to #50. EPSR illegal use error Signed-off-by: Adam Fowler --- .vscode/settings.json | 3 +- config/zephyr/nRF52832.conf | 5 +- config/zephyr/nRF52833.conf | 5 +- config/zephyr/nRF52840.conf | 5 +- config/zephyr/nRF5340.conf | 4 +- config/zephyr/receiver.conf | 3 - herald-tests-zephyr/CMakeLists.txt | 37 +-- herald-tests-zephyr/src/main.cpp | 2 +- herald-tests/CMakeLists.txt | 5 +- herald-tests/datatypes-tests.cpp | 234 +----------------- herald-tests/datatypesdataderived-tests.cpp | 59 +++++ herald-tests/datetime-tests.cpp | 96 +++++++ herald-tests/randomuuid-tests.cpp | 61 +++++ .../include/herald/datatype/base64_string.h | 7 +- herald/src/datatype/base64_string.cpp | 40 +-- 15 files changed, 298 insertions(+), 268 deletions(-) create mode 100644 herald-tests/datatypesdataderived-tests.cpp create mode 100644 herald-tests/datetime-tests.cpp create mode 100644 herald-tests/randomuuid-tests.cpp diff --git a/.vscode/settings.json b/.vscode/settings.json index 48ee4e8..780ffac 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -82,6 +82,7 @@ "filesystem": "cpp", "strstream": "cpp", "resumable": "cpp", - "codecvt": "cpp" + "codecvt": "cpp", + "*.rh": "cpp" } } \ No newline at end of file diff --git a/config/zephyr/nRF52832.conf b/config/zephyr/nRF52832.conf index d27b69d..9895766 100644 --- a/config/zephyr/nRF52832.conf +++ b/config/zephyr/nRF52832.conf @@ -15,4 +15,7 @@ CONFIG_TINYCRYPT_SHA256=y # Simple Payload Support ENDS #CONFIG_BT_MAX_CONN=20 -CONFIG_BT_MAX_CONN=5 \ No newline at end of file +CONFIG_BT_MAX_CONN=5 + +# Fix for use of heap in gatt_gm erroring with undefined reference to k_aligned_alloc +CONFIG_HEAP_MEM_POOL_SIZE=256 \ No newline at end of file diff --git a/config/zephyr/nRF52833.conf b/config/zephyr/nRF52833.conf index 5ec3548..fde1ec2 100644 --- a/config/zephyr/nRF52833.conf +++ b/config/zephyr/nRF52833.conf @@ -15,4 +15,7 @@ CONFIG_TINYCRYPT_SHA256=y # Simple Payload Support ENDS #CONFIG_BT_MAX_CONN=20 -CONFIG_BT_MAX_CONN=10 \ No newline at end of file +CONFIG_BT_MAX_CONN=10 + +# Fix for use of heap in gatt_gm erroring with undefined reference to k_aligned_alloc +CONFIG_HEAP_MEM_POOL_SIZE=256 \ No newline at end of file diff --git a/config/zephyr/nRF52840.conf b/config/zephyr/nRF52840.conf index b7c190d..afcf350 100644 --- a/config/zephyr/nRF52840.conf +++ b/config/zephyr/nRF52840.conf @@ -20,4 +20,7 @@ CONFIG_TINYCRYPT_SHA256=y # Simple Payload Support ENDS #CONFIG_BT_MAX_CONN=20 -CONFIG_BT_MAX_CONN=10 \ No newline at end of file +CONFIG_BT_MAX_CONN=10 + +# Fix for use of heap in gatt_gm erroring with undefined reference to k_aligned_alloc +CONFIG_HEAP_MEM_POOL_SIZE=256 \ No newline at end of file diff --git a/config/zephyr/nRF5340.conf b/config/zephyr/nRF5340.conf index 5edf953..9f701c1 100644 --- a/config/zephyr/nRF5340.conf +++ b/config/zephyr/nRF5340.conf @@ -24,4 +24,6 @@ CONFIG_BT_MAX_CONN=10 # Requirement of ncs/v1.5.0/zephyr/drivers/bluetooth/hci/rpmsg_nrf53.c -CONFIG_HEAP_MEM_POOL_SIZE=1024 \ No newline at end of file +# Fix for use of heap in gatt_gm erroring with undefined reference to k_aligned_alloc +CONFIG_HEAP_MEM_POOL_SIZE=16384 +# 5340 has lots of RAM, so use it \ No newline at end of file diff --git a/config/zephyr/receiver.conf b/config/zephyr/receiver.conf index 4938f1a..92cde2c 100644 --- a/config/zephyr/receiver.conf +++ b/config/zephyr/receiver.conf @@ -8,8 +8,5 @@ CONFIG_BT_GATT_DM=y # Scanning CONFIG_BT_SCAN=y -# Fix for use of heap in gatt_gm erroring with undefined reference to k_aligned_alloc -CONFIG_HEAP_MEM_POOL_SIZE=256 - # Issue #50 to prevent stack overflow in CPU CONFIG_BT_RX_STACK_SIZE=8192 diff --git a/herald-tests-zephyr/CMakeLists.txt b/herald-tests-zephyr/CMakeLists.txt index 352cbeb..51da2c4 100644 --- a/herald-tests-zephyr/CMakeLists.txt +++ b/herald-tests-zephyr/CMakeLists.txt @@ -106,32 +106,41 @@ target_sources(app PRIVATE # Now tests source # root with main - #../herald-tests/datatypes-tests.cpp - # ^^^ FIX THIS FROM DYING ALL THE TIME -> MAY NEED TO REWRITE DATA CLASS - SEE STL STRING TRICK + #../herald-tests/datatypes-tests.cpp # Passes when split out on 21 Mar 2021 + #../herald-tests/datetime-tests.cpp # Passes when split out on 21 Mar 2021 + #../herald-tests/randomuuid-tests.cpp # FAILS silently 21 Mar 2021 # Low level - # ../herald-tests/sensorlogger-tests.cpp # Don't run this on Zephyr + #../herald-tests/sensorlogger-tests.cpp # Passes on its own 21 Mar 2021 + #../herald-tests/data-tests.cpp # Passes on its own 21 Mar 2021 + #../herald-tests/datatypesdataderived-tests.cpp # Passes on its own 21 Mar 2021 + # These three passed together on 21 Mar 2021 #../herald-tests/errorcontactlog-tests.cpp - #../herald-tests/data-tests.cpp #../herald-tests/blemacaddress-tests.cpp #../herald-tests/targetidentifier-tests.cpp # mid level - ../herald-tests/analysisrunner-tests.cpp - #../herald-tests/analysissensor-tests.cpp + #../herald-tests/analysisrunner-tests.cpp # Passes on its own 21 Mar 2021 + #../herald-tests/analysissensor-tests.cpp # Passes on its own 21 Mar 2021 + # These three passed together on 21 Mar 2021 #../herald-tests/beaconpayload-tests.cpp #../herald-tests/extendeddata-tests.cpp #../herald-tests/fixedpayload-tests.cpp - #../herald-tests/simplepayload-tests.cpp - #../herald-tests/bledevice-tests.cpp - #../herald-tests/sample-tests.cpp - #../herald-tests/ranges-tests.cpp + #../herald-tests/simplepayload-tests.cpp # Passes on its own 21 Mar 2021 + #../herald-tests/bledevice-tests.cpp # SILENTLY FAILS on its own 21 Mar 2021 + #../herald-tests/sample-tests.cpp # Passes on its own 21 Mar 2021 (May require hci reflash too) + #../herald-tests/ranges-tests.cpp # FAILS on its own 21 Mar 2021 - Breaks the programmer! Also hard faults. # high level - #../herald-tests/advertparser-tests.cpp - #../herald-tests/bledatabase-tests.cpp - #../herald-tests/blecoordinator-tests.cpp - #../herald-tests/coordinator-tests.cpp + # These four passed together on 21 Mar 2021 + ../herald-tests/advertparser-tests.cpp + ../herald-tests/bledatabase-tests.cpp + ../herald-tests/blecoordinator-tests.cpp + ../herald-tests/coordinator-tests.cpp + + # NOTE: All tests that passed individually on 21 Mar 2021 also passed all together in 15 seconds + # This means that the issue is the individual tests, not catch or zephyr specifically, and not RAM issues. + # All together they used 707KB on Flash for the merged.hex file on a nRF5340dk build done on Windows 10. # now main func src/main.cpp diff --git a/herald-tests-zephyr/src/main.cpp b/herald-tests-zephyr/src/main.cpp index 8dcf5b5..9430c2c 100644 --- a/herald-tests-zephyr/src/main.cpp +++ b/herald-tests-zephyr/src/main.cpp @@ -116,7 +116,7 @@ void main(void) LOG_DBG("Initialising catch"); k_sleep(K_SECONDS(2)); - k_tid_t herald_pid = k_thread_create(&herald_thread, herald_stack, 8192, + k_tid_t herald_pid = k_thread_create(&herald_thread, herald_stack, 16384, (k_thread_entry_t)herald_entry, NULL, NULL, NULL, -1, K_USER, K_SECONDS(4)); diff --git a/herald-tests/CMakeLists.txt b/herald-tests/CMakeLists.txt index 49fc8f3..6bde891 100644 --- a/herald-tests/CMakeLists.txt +++ b/herald-tests/CMakeLists.txt @@ -1,13 +1,16 @@ cmake_minimum_required(VERSION 3.12) add_executable(herald-tests - # root with main + # base data types datatypes-tests.cpp + datetime-tests.cpp + randomuuid-tests.cpp # Low level sensorlogger-tests.cpp errorcontactlog-tests.cpp data-tests.cpp + datatypesdataderived-tests.cpp blemacaddress-tests.cpp targetidentifier-tests.cpp diff --git a/herald-tests/datatypes-tests.cpp b/herald-tests/datatypes-tests.cpp index 26f32fd..721da2f 100644 --- a/herald-tests/datatypes-tests.cpp +++ b/herald-tests/datatypes-tests.cpp @@ -1,20 +1,7 @@ -// Copyright 2020 VMware, Inc. +// Copyright 2020-2021 Herald Project Contributors // SPDX-License-Identifier: Apache-2.0 // -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - #include "catch.hpp" #include "herald/herald.h" @@ -44,33 +31,6 @@ TEST_CASE("datatypes-proximity-basics", "[datatypes][proximity][basics]") { -TEST_CASE("datatypes-payloaddata-basics", "[datatypes][payloaddata][basics]") { - SECTION("datatypes-payloaddata-basics") { - herald::datatype::PayloadData payload{}; // empty default ctor - - REQUIRE(payload.size() == 0); - - - herald::datatype::PayloadData payload2{std::byte('a'), 6}; // repeating byte ctor - - REQUIRE(payload2.size() == 6); - REQUIRE(payload2.at(0) == std::byte('a')); - REQUIRE(payload2.at(5) == std::byte('a')); - - const char* charArray = "wotcha"; - std::byte byteArray[6]; - for (int i = 0;i < 6;i++) { - byteArray[i] = std::byte(charArray[i]); - } - herald::datatype::PayloadData payload3{byteArray, 4}; - - REQUIRE(payload3.size() == 4); - REQUIRE(payload3.at(0) == std::byte('w')); - REQUIRE(payload3.at(3) == std::byte('c')); - } -} - - @@ -96,36 +56,6 @@ TEST_CASE("datatypes-encounter-basics", "[datatypes][encounter][basics]") { -TEST_CASE("datatypes-immediatesenddata-basics", "[datatypes][immediatesenddata][basics]") { - SECTION("datatypes-immediatesenddata-basics") { - herald::datatype::Data d{std::byte('f'),8}; - herald::datatype::ImmediateSendData isd(d); - - REQUIRE(isd.size() == 8); - REQUIRE(isd.at(0) == std::byte('f')); - REQUIRE(isd.at(7) == std::byte('f')); - } -} - - -TEST_CASE("datatypes-date-basics", "[datatypes][date][basics]") { - SECTION("datatypes-date-basics") { - herald::datatype::Date d(1608483600); // long ctor - - REQUIRE(d.secondsSinceUnixEpoch() == 1608483600); - REQUIRE(d.iso8601DateTime() == std::string("2020-12-20T17:00:00Z")); - REQUIRE(((std::string)d) == std::string("2020-12-20T17:00:00Z")); - - herald::datatype::Date d2(d); // copy ctor - REQUIRE(d2.secondsSinceUnixEpoch() == 1608483600); - REQUIRE(d2.iso8601DateTime() == std::string("2020-12-20T17:00:00Z")); - REQUIRE(((std::string)d2) == std::string("2020-12-20T17:00:00Z")); - - // TODO Default constructor producing 'now' - } -} - - TEST_CASE("datatypes-errorcode-ctor-default", "[datatypes][errorcode][ctor-default]") { SECTION("datatypes-errorcode-ctor-default") { @@ -188,18 +118,6 @@ TEST_CASE("datatypes-rssi-ctor-copy", "[datatypes][rssi][ctor][copy]") { -TEST_CASE("datatypes-payloadsharingdata-basics", "[datatypes][payloadsharingdata][ctor][basics]") { - SECTION("datatypes-payloadsharingdata-basics") { - herald::datatype::PayloadSharingData psd{{11},{std::byte('g'),4}}; - - REQUIRE(psd.rssi.intValue() == 11); - REQUIRE(psd.data.size() == 4); - REQUIRE(psd.data.at(3) == std::byte('g')); - } -} - - - TEST_CASE("datatypes-placename-basics", "[datatypes][placename][ctor][basics]") { @@ -215,149 +133,23 @@ TEST_CASE("datatypes-placename-basics", "[datatypes][placename][ctor][basics]") -TEST_CASE("datatypes-timeinterval-basics", "[datatypes][timeinterval][ctor][basics]") { - SECTION("datatypes-timeinterval-basics") { - herald::datatype::TimeInterval ti{1200}; - REQUIRE(ti.millis() == 1'200'000); - auto t2 = herald::datatype::TimeInterval::never(); - REQUIRE(t2.millis() == LONG_MAX); - REQUIRE(((std::string)t2) == std::string("never")); - auto t3 = herald::datatype::TimeInterval::minutes(20); - REQUIRE(t3.millis() == 20 * 60 * 1000); - auto t4 = herald::datatype::TimeInterval::seconds(20); - REQUIRE(t4.millis() == 20 * 1000); +// TEST_CASE("datatypes-memory-use","[datatypes][memory]") { - auto t5 = herald::datatype::TimeInterval::zero(); - REQUIRE(t5.millis() == 0); +// SECTION("datatypes-memory-use") { +// // TODO always output sizes to a CSV report file - herald::datatype::Date d1{1000}; - herald::datatype::Date d2{1200}; - herald::datatype::TimeInterval t6(d1,d2); +// using namespace herald::datatype; +// Base64String b64 = Base64String::encode(Data(std::byte(2),8)); +// INFO("Base64String size for 8 chars: " << sizeof(b64)); +// REQUIRE(sizeof(b64) <= 40); // std::string of ~12 chars plus 64 bit size +// Data d{std::byte(1),32}; +// INFO("Data size for 32 bytes: " << sizeof(d)); +// REQUIRE(sizeof(d) <= 40); - REQUIRE(t6.millis() == 200 * 1000); - REQUIRE(((std::string)t6) == std::string("200")); - - REQUIRE(t5 < ti); - REQUIRE(t5 < t2); - REQUIRE(t5 < t3); - REQUIRE(!(t5 > t3)); - REQUIRE(t5 < t4); - } -} - - -TEST_CASE("datatypes-timeinterval-date", "[datatypes][timeinterval][ctor][date]") { - SECTION("datatypes-timeinterval-date") { - herald::datatype::Date earlier{1200}; - herald::datatype::Date now{1500}; - - herald::datatype::TimeInterval difference{earlier,now}; - REQUIRE(difference.seconds() == 300); - - herald::datatype::TimeInterval reverseDifference{now,earlier}; - REQUIRE(reverseDifference.seconds() == -300); - - herald::datatype::Date advanced = earlier + difference; - REQUIRE(advanced == now); - } -} - - -TEST_CASE("datatypes-timeinterval-daterelative", "[datatypes][timeinterval][ctor][daterelative]") { - SECTION("datatypes-timeinterval-daterelative") { - herald::datatype::Date now; - herald::datatype::TimeInterval threeHundred(300); - herald::datatype::Date earlier = now - threeHundred; - - herald::datatype::TimeInterval difference{earlier,now}; - REQUIRE(difference.seconds() == 300); - - herald::datatype::TimeInterval reverseDifference{now,earlier}; - REQUIRE(reverseDifference.seconds() == -300); - - herald::datatype::Date advanced = earlier + difference; - REQUIRE(advanced == now); - } -} - - - - -// TEST_CASE("datatypes-uuid-basics", "[datatypes][uuid][ctor][basics]") { -// SECTION("datatypes-uuid-basics") { -// auto uuid1 = herald::datatype::UUID::random(); -// REQUIRE(uuid1.valid()); -// REQUIRE(uuid1.string().size() == 36); // 4 hyphens, 16 hex bytes = 36 characters +// // TODO other types here // } -// } - - -TEST_CASE("random-allzeros","[randomness][allzeros][basic][datatypes]") { - - SECTION("random-allzeros-basic") { - herald::datatype::AllZerosNotRandom rnd; - REQUIRE(rnd.nextInt() == 0); - REQUIRE(rnd.nextDouble() == 0); - std::vector zeros; - for (int i = 0;i < 4;i++) { - zeros.push_back(std::byte(0)); - } - herald::datatype::Data expected(zeros); - herald::datatype::Data toFill; - rnd.nextBytes(4, toFill); - REQUIRE(toFill == expected); - } -} - - - - -TEST_CASE("datatypes-uuid-notrandom","[randomness][uuid][basic][datatypes]") { - - SECTION("datatypes-uuid-notrandom") { - std::unique_ptr rnd = std::make_unique(); - herald::datatype::RandomnessGenerator gen(std::move(rnd)); - auto emptyV4 = herald::datatype::UUID::random(gen); - REQUIRE(emptyV4.string() == std::string("00000000-0000-4000-8000-000000000000")); // v4 variant 1 - } -} - - - - -TEST_CASE("datatypes-uuid-random","[randomness][uuid][basic][datatypes]") { - - SECTION("datatypes-uuid-random") { - std::unique_ptr rnd = - std::make_unique(); - herald::datatype::RandomnessGenerator gen(std::move(rnd)); - auto randomV4 = herald::datatype::UUID::random(gen); - std::string str = randomV4.string(); - INFO("UUID v4 random value: " << str); - REQUIRE(str != std::string("00000000-0000-4000-8000-000000000000")); // v4 variant 1 - } -} - - - - -TEST_CASE("datatypes-memory-use","[datatypes][memory]") { - - SECTION("datatypes-memory-use") { - // TODO always output sizes to a CSV report file - - using namespace herald::datatype; - Base64String b64 = Base64String::encode(Data(std::byte(2),8)); - INFO("Base64String size for 8 chars: " << sizeof(b64)); - REQUIRE(sizeof(b64) <= 16); // 8 chars plus 64 bit size - Data d{std::byte(1),32}; - INFO("Data size for 32 bytes: " << sizeof(d)); - REQUIRE(sizeof(d) <= 40); - - // TODO other types here - } -} \ No newline at end of file +// } \ No newline at end of file diff --git a/herald-tests/datatypesdataderived-tests.cpp b/herald-tests/datatypesdataderived-tests.cpp new file mode 100644 index 0000000..bc69e36 --- /dev/null +++ b/herald-tests/datatypesdataderived-tests.cpp @@ -0,0 +1,59 @@ +// Copyright 2020-2021 Herald Project Contributors +// SPDX-License-Identifier: Apache-2.0 +// + +#include "catch.hpp" + +#include "herald/herald.h" + + +TEST_CASE("datatypes-payloaddata-basics", "[datatypes][payloaddata][basics]") { + SECTION("datatypes-payloaddata-basics") { + herald::datatype::PayloadData payload{}; // empty default ctor + + REQUIRE(payload.size() == 0); + + + herald::datatype::PayloadData payload2{std::byte('a'), 6}; // repeating byte ctor + + REQUIRE(payload2.size() == 6); + REQUIRE(payload2.at(0) == std::byte('a')); + REQUIRE(payload2.at(5) == std::byte('a')); + + const char* charArray = "wotcha"; + std::byte byteArray[6]; + for (int i = 0;i < 6;i++) { + byteArray[i] = std::byte(charArray[i]); + } + herald::datatype::PayloadData payload3{byteArray, 4}; + + REQUIRE(payload3.size() == 4); + REQUIRE(payload3.at(0) == std::byte('w')); + REQUIRE(payload3.at(3) == std::byte('c')); + } +} + + + +TEST_CASE("datatypes-immediatesenddata-basics", "[datatypes][immediatesenddata][basics]") { + SECTION("datatypes-immediatesenddata-basics") { + herald::datatype::Data d{std::byte('f'),8}; + herald::datatype::ImmediateSendData isd(d); + + REQUIRE(isd.size() == 8); + REQUIRE(isd.at(0) == std::byte('f')); + REQUIRE(isd.at(7) == std::byte('f')); + } +} + + +TEST_CASE("datatypes-payloadsharingdata-basics", "[datatypes][payloadsharingdata][ctor][basics]") { + SECTION("datatypes-payloadsharingdata-basics") { + herald::datatype::PayloadSharingData psd{{11},{std::byte('g'),4}}; + + REQUIRE(psd.rssi.intValue() == 11); + REQUIRE(psd.data.size() == 4); + REQUIRE(psd.data.at(3) == std::byte('g')); + } +} + diff --git a/herald-tests/datetime-tests.cpp b/herald-tests/datetime-tests.cpp new file mode 100644 index 0000000..d21d3a8 --- /dev/null +++ b/herald-tests/datetime-tests.cpp @@ -0,0 +1,96 @@ +// Copyright 2020-2021 Herald Project Contributors +// SPDX-License-Identifier: Apache-2.0 +// + +#include "catch.hpp" + +#include "herald/herald.h" + + +TEST_CASE("datatypes-date-basics", "[datatypes][date][basics]") { + SECTION("datatypes-date-basics") { + herald::datatype::Date d(1608483600); // long ctor + + REQUIRE(d.secondsSinceUnixEpoch() == 1608483600); + REQUIRE(d.iso8601DateTime() == std::string("2020-12-20T17:00:00Z")); + REQUIRE(((std::string)d) == std::string("2020-12-20T17:00:00Z")); + + herald::datatype::Date d2(d); // copy ctor + REQUIRE(d2.secondsSinceUnixEpoch() == 1608483600); + REQUIRE(d2.iso8601DateTime() == std::string("2020-12-20T17:00:00Z")); + REQUIRE(((std::string)d2) == std::string("2020-12-20T17:00:00Z")); + + // TODO Default constructor producing 'now' + } +} + + + +TEST_CASE("datatypes-timeinterval-basics", "[datatypes][timeinterval][ctor][basics]") { + SECTION("datatypes-timeinterval-basics") { + herald::datatype::TimeInterval ti{1200}; + + REQUIRE(ti.millis() == 1'200'000); + + auto t2 = herald::datatype::TimeInterval::never(); + REQUIRE(t2.millis() == LONG_MAX); + REQUIRE(((std::string)t2) == std::string("never")); + + auto t3 = herald::datatype::TimeInterval::minutes(20); + REQUIRE(t3.millis() == 20 * 60 * 1000); + + auto t4 = herald::datatype::TimeInterval::seconds(20); + REQUIRE(t4.millis() == 20 * 1000); + + auto t5 = herald::datatype::TimeInterval::zero(); + REQUIRE(t5.millis() == 0); + + herald::datatype::Date d1{1000}; + herald::datatype::Date d2{1200}; + herald::datatype::TimeInterval t6(d1,d2); + + REQUIRE(t6.millis() == 200 * 1000); + REQUIRE(((std::string)t6) == std::string("200")); + + REQUIRE(t5 < ti); + REQUIRE(t5 < t2); + REQUIRE(t5 < t3); + REQUIRE(!(t5 > t3)); + REQUIRE(t5 < t4); + } +} + + +TEST_CASE("datatypes-timeinterval-date", "[datatypes][timeinterval][ctor][date]") { + SECTION("datatypes-timeinterval-date") { + herald::datatype::Date earlier{1200}; + herald::datatype::Date now{1500}; + + herald::datatype::TimeInterval difference{earlier,now}; + REQUIRE(difference.seconds() == 300); + + herald::datatype::TimeInterval reverseDifference{now,earlier}; + REQUIRE(reverseDifference.seconds() == -300); + + herald::datatype::Date advanced = earlier + difference; + REQUIRE(advanced == now); + } +} + + +TEST_CASE("datatypes-timeinterval-daterelative", "[datatypes][timeinterval][ctor][daterelative]") { + SECTION("datatypes-timeinterval-daterelative") { + herald::datatype::Date now; + herald::datatype::TimeInterval threeHundred(300); + herald::datatype::Date earlier = now - threeHundred; + + herald::datatype::TimeInterval difference{earlier,now}; + REQUIRE(difference.seconds() == 300); + + herald::datatype::TimeInterval reverseDifference{now,earlier}; + REQUIRE(reverseDifference.seconds() == -300); + + herald::datatype::Date advanced = earlier + difference; + REQUIRE(advanced == now); + } +} \ No newline at end of file diff --git a/herald-tests/randomuuid-tests.cpp b/herald-tests/randomuuid-tests.cpp new file mode 100644 index 0000000..f730234 --- /dev/null +++ b/herald-tests/randomuuid-tests.cpp @@ -0,0 +1,61 @@ +// Copyright 2020-2021 Herald Project Contributors +// SPDX-License-Identifier: Apache-2.0 +// + +#include + +#include "catch.hpp" + +#include "herald/herald.h" + + +// TEST_CASE("datatypes-uuid-basics", "[datatypes][uuid][ctor][basics]") { +// SECTION("datatypes-uuid-basics") { +// auto uuid1 = herald::datatype::UUID::random(); +// REQUIRE(uuid1.valid()); +// REQUIRE(uuid1.string().size() == 36); // 4 hyphens, 16 hex bytes = 36 characters +// } +// } + + +TEST_CASE("random-allzeros","[randomness][allzeros][basic][datatypes]") { + + SECTION("random-allzeros-basic") { + herald::datatype::AllZerosNotRandom rnd; + REQUIRE(rnd.nextInt() == 0); + REQUIRE(rnd.nextDouble() == 0); + herald::datatype::Data expected(std::byte(0),4); + herald::datatype::Data toFill; + rnd.nextBytes(4, toFill); + REQUIRE(toFill == expected); + } +} + + + + +TEST_CASE("datatypes-uuid-notrandom","[randomness][uuid][basic][datatypes]") { + + SECTION("datatypes-uuid-notrandom") { + std::unique_ptr rnd = std::make_unique(); + herald::datatype::RandomnessGenerator gen(std::move(rnd)); + auto emptyV4 = herald::datatype::UUID::random(gen); + REQUIRE(emptyV4.string() == std::string("00000000-0000-4000-8000-000000000000")); // v4 variant 1 + } +} + + + + +TEST_CASE("datatypes-uuid-random","[randomness][uuid][basic][datatypes]") { + + SECTION("datatypes-uuid-random") { + std::unique_ptr rnd = + std::make_unique(); + herald::datatype::RandomnessGenerator gen(std::move(rnd)); + auto randomV4 = herald::datatype::UUID::random(gen); + std::string str = randomV4.string(); + INFO("UUID v4 random value: " << str); + REQUIRE(str != std::string("00000000-0000-4000-8000-000000000000")); // v4 variant 1 + } +} diff --git a/herald/include/herald/datatype/base64_string.h b/herald/include/herald/datatype/base64_string.h index c24aaba..7313a40 100644 --- a/herald/include/herald/datatype/base64_string.h +++ b/herald/include/herald/datatype/base64_string.h @@ -37,9 +37,10 @@ class Base64String { /// \brief Returns the Base64 encoding of this class as a std::string std::string encoded() const noexcept; // Return base64 string representation (copy of, not reference to) private: - /// \brief PIMPL Idiom - hidden internal class for Base64String for ABI compatibility - class Impl; - std::unique_ptr mImpl; // PIMPL IDIOM + // /// \brief PIMPL Idiom - hidden internal class for Base64String for ABI compatibility + // class Impl; + // std::unique_ptr mImpl; // PIMPL IDIOM + std::string value; // Base64 encoded, and guarded }; } // end namespace diff --git a/herald/src/datatype/base64_string.cpp b/herald/src/datatype/base64_string.cpp index 4676fef..a3c3b95 100644 --- a/herald/src/datatype/base64_string.cpp +++ b/herald/src/datatype/base64_string.cpp @@ -12,15 +12,15 @@ namespace herald { namespace datatype { // IMPL DEFINITION -class Base64String::Impl { -public: - Impl(); - ~Impl() = default; +// class Base64String::Impl { +// public: +// Impl(); +// ~Impl() = default; - std::string value; // Base64 encoded, and guarded -}; +// std::string value; // Base64 encoded, and guarded +// }; -Base64String::Impl::Impl() { } +// Base64String::Impl::Impl() { } const std::string base64_chars = @@ -45,19 +45,19 @@ Base64String::from(const std::string& original, Base64String& toInitialise) noex if (!ok) { return false; } - toInitialise.mImpl->value = original; + toInitialise.value = original; return true; } -Base64String::Base64String() : mImpl(std::make_unique()) { } +Base64String::Base64String() : value() { } Base64String::Base64String(Base64String&& other) - : mImpl(std::make_unique()) -{ - mImpl->value = std::move(other.mImpl->value); + : value(std::move(other.value)) +{ + ; } -Base64String::~Base64String() { } +Base64String::~Base64String() = default; Base64String Base64String::encode(const Data& from) noexcept { @@ -135,22 +135,22 @@ Base64String::encode(const Data& from) noexcept { // buffer.append("="); // } - Base64String value; - value.mImpl->value = std::move(ret); - return value; + Base64String nvalue; + nvalue.value = std::move(ret); + return nvalue; } Data Base64String::decode() const noexcept { - std::size_t in_len = mImpl->value.size(); + std::size_t in_len = value.size(); int i = 0; int j = 0; int in_ = 0; char char_array_4[4], char_array_3[3]; std::vector ret; - while (in_len-- && ( mImpl->value[in_] != '=') && is_base64(mImpl->value[in_])) { - char_array_4[i++] = mImpl->value[in_]; in_++; + while (in_len-- && ( value[in_] != '=') && is_base64(value[in_])) { + char_array_4[i++] = value[in_]; in_++; if (i ==4) { for (i = 0; i <4; i++) char_array_4[i] = (char)base64_chars.find(char_array_4[i]); @@ -186,7 +186,7 @@ Base64String::decode() const noexcept { std::string Base64String::encoded() const noexcept { - return mImpl->value; // copy ctor + return value; // copy ctor } From e2f14a46e3524766e64866dadefa8761d9da6aa1 Mon Sep 17 00:00:00 2001 From: Adam Fowler Date: Sun, 21 Mar 2021 17:10:21 +0000 Subject: [PATCH 15/28] Related to #50. Wearable sample running on nRF5340 Disabled analysis runner for now due to memory issue (use of std::move likely the culprit). I have enabled JLink debugging within VSCode now too. Brilliant! Signed-off-by: Adam Fowler --- .vscode/launch.json | 2 +- herald-wearable/.vscode/launch.json | 35 +++++++++++++++++++++++++++++ herald-wearable/src/main.cpp | 24 ++++++++++---------- 3 files changed, 48 insertions(+), 13 deletions(-) create mode 100644 herald-wearable/.vscode/launch.json diff --git a/.vscode/launch.json b/.vscode/launch.json index 3f03198..c653f11 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -20,4 +20,4 @@ } } ] -} \ No newline at end of file +} diff --git a/herald-wearable/.vscode/launch.json b/herald-wearable/.vscode/launch.json new file mode 100644 index 0000000..a248b01 --- /dev/null +++ b/herald-wearable/.vscode/launch.json @@ -0,0 +1,35 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "type": "gnu-debugger", + "request": "launch", + "name": "GNU debugger", + "program": "${workspaceFolder}/build/zephyr/zephyr.elf", + "toolchain": "C:/gnuarmemb/bin", // was "${config:arm-none-eabi.bin}" + "client": "arm-none-eabi-gdb.exe", + "server": "JLinkGDBServer", + "windows": { + "server": "C:/Program Files (x86)/SEGGER/JLink/JLinkGDBServerCL.exe", + }, + "serverArgs": [ + "-device", "NRF5340_XXAA_APP", + "-if", "SWD", + "-speed", "4000" + ], + "serverHost": "localhost", + "serverPort": 2331, + "customVariables": [ + "port0", + "port1", + "port2", + ], + "autoRun": false, + "debugOutput": false //, + //"preLaunchTask": "build firmware" + } + ] +} \ No newline at end of file diff --git a/herald-wearable/src/main.cpp b/herald-wearable/src/main.cpp index 038c88a..bacc2d5 100644 --- a/herald-wearable/src/main.cpp +++ b/herald-wearable/src/main.cpp @@ -299,20 +299,20 @@ void herald_entry() { // 4. Now create a live analysis pipeline and enable RSSI to be sent to it for distance estimation - herald::analysis::algorithms::distance::FowlerBasicAnalyser distanceAnalyser(0, -50, -24); // 0 = run every time run() is called + // herald::analysis::algorithms::distance::FowlerBasicAnalyser distanceAnalyser(0, -50, -24); // 0 = run every time run() is called - herald::analysis::LoggingAnalysisDelegate myDelegate(ctx); - herald::analysis::AnalysisDelegateManager adm(std::move(myDelegate)); // NOTE: myDelegate MOVED FROM and no longer accessible - herald::analysis::AnalysisProviderManager apm(std::move(distanceAnalyser)); // NOTE: distanceAnalyser MOVED FROM and no longer accessible + // herald::analysis::LoggingAnalysisDelegate myDelegate(ctx); + // herald::analysis::AnalysisDelegateManager adm(std::move(myDelegate)); // NOTE: myDelegate MOVED FROM and no longer accessible + // herald::analysis::AnalysisProviderManager apm(std::move(distanceAnalyser)); // NOTE: distanceAnalyser MOVED FROM and no longer accessible - herald::analysis::AnalysisRunner< - herald::analysis::AnalysisDelegateManager>, - herald::analysis::AnalysisProviderManager, - RSSI,Distance - > runner(adm, apm); // just for Sample types, and their produced output (Sample) + // herald::analysis::AnalysisRunner< + // herald::analysis::AnalysisDelegateManager>, + // herald::analysis::AnalysisProviderManager, + // RSSI,Distance + // > runner(adm, apm); // just for Sample types, and their produced output (Sample) - std::shared_ptr> src = std::make_shared>(runner); - sa->add(src); + // std::shared_ptr> src = std::make_shared>(runner); + // sa->add(src); LOG_DBG("Starting sensor array"); @@ -334,7 +334,7 @@ void herald_entry() { if (0 == iter % (5000 / delay)) { LOG_DBG("herald thread still running. Iteration: %d", iter); - runner.run(Date()); // Note: You may want to do this less or more regularly depending on your requirements + // runner.run(Date()); // Note: You may want to do this less or more regularly depending on your requirements } last = now; From 8459b6e05e2d8ae7952823a4cb7940b0c487cdbd Mon Sep 17 00:00:00 2001 From: Adam Fowler Date: Sun, 21 Mar 2021 20:57:38 +0000 Subject: [PATCH 16/28] Added launch config and enabled analysis to facilitate debugging Signed-off-by: Adam Fowler --- herald-wearable/.vscode/launch.json | 4 ++-- herald-wearable/src/main.cpp | 22 +++++++++++----------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/herald-wearable/.vscode/launch.json b/herald-wearable/.vscode/launch.json index a248b01..4637827 100644 --- a/herald-wearable/.vscode/launch.json +++ b/herald-wearable/.vscode/launch.json @@ -7,9 +7,9 @@ { "type": "gnu-debugger", "request": "launch", - "name": "GNU debugger", + "name": "Debug - NRF5340_XXAA_APP - Launch", "program": "${workspaceFolder}/build/zephyr/zephyr.elf", - "toolchain": "C:/gnuarmemb/bin", // was "${config:arm-none-eabi.bin}" + "toolchain": "${config:armToolchainPath}/bin", "client": "arm-none-eabi-gdb.exe", "server": "JLinkGDBServer", "windows": { diff --git a/herald-wearable/src/main.cpp b/herald-wearable/src/main.cpp index bacc2d5..7d236fc 100644 --- a/herald-wearable/src/main.cpp +++ b/herald-wearable/src/main.cpp @@ -299,20 +299,20 @@ void herald_entry() { // 4. Now create a live analysis pipeline and enable RSSI to be sent to it for distance estimation - // herald::analysis::algorithms::distance::FowlerBasicAnalyser distanceAnalyser(0, -50, -24); // 0 = run every time run() is called + herald::analysis::algorithms::distance::FowlerBasicAnalyser distanceAnalyser(0, -50, -24); // 0 = run every time run() is called - // herald::analysis::LoggingAnalysisDelegate myDelegate(ctx); - // herald::analysis::AnalysisDelegateManager adm(std::move(myDelegate)); // NOTE: myDelegate MOVED FROM and no longer accessible - // herald::analysis::AnalysisProviderManager apm(std::move(distanceAnalyser)); // NOTE: distanceAnalyser MOVED FROM and no longer accessible + herald::analysis::LoggingAnalysisDelegate myDelegate(ctx); + herald::analysis::AnalysisDelegateManager adm(std::move(myDelegate)); // NOTE: myDelegate MOVED FROM and no longer accessible + herald::analysis::AnalysisProviderManager apm(std::move(distanceAnalyser)); // NOTE: distanceAnalyser MOVED FROM and no longer accessible - // herald::analysis::AnalysisRunner< - // herald::analysis::AnalysisDelegateManager>, - // herald::analysis::AnalysisProviderManager, - // RSSI,Distance - // > runner(adm, apm); // just for Sample types, and their produced output (Sample) + herald::analysis::AnalysisRunner< + herald::analysis::AnalysisDelegateManager>, + herald::analysis::AnalysisProviderManager, + RSSI,Distance + > runner(adm, apm); // just for Sample types, and their produced output (Sample) - // std::shared_ptr> src = std::make_shared>(runner); - // sa->add(src); + std::shared_ptr> src = std::make_shared>(runner); + sa->add(src); LOG_DBG("Starting sensor array"); From 2ee9045fd0be29cd15d8c23f82b7191a4ca3da5a Mon Sep 17 00:00:00 2001 From: Adam Fowler Date: Sat, 27 Mar 2021 14:49:31 +0000 Subject: [PATCH 17/28] Mostly refactored all Context related classes for templates Known issues:- - Herald tests not compiling - Some herald refactored classes need code moving from cpp to header files - Not every pointer based class moved to templates yet Signed-off-by: Adam Fowler --- herald-tests/CMakeLists.txt | 9 +- herald-tests/analysissensor-tests.cpp | 44 +-- herald-tests/blecoordinator-tests.cpp | 153 ++++---- herald-tests/bledatabase-tests.cpp | 22 +- herald-tests/coordinator-tests.cpp | 45 ++- herald-tests/errorcontactlog-tests.cpp | 74 ++-- herald-tests/sensorlogger-tests.cpp | 156 ++++---- herald-tests/simplepayload-tests.cpp | 14 +- herald-tests/test-templates.h | 40 ++ herald-wearable/CMakeLists.txt | 2 +- herald/herald.cmake | 10 +- .../analysis/logging_analysis_delegate.h | 8 +- herald/include/herald/ble/ble_concrete.h | 20 +- herald/include/herald/ble/ble_coordinator.h | 3 +- herald/include/herald/context.h | 89 +++-- herald/include/herald/data/contact_log.h | 52 ++- herald/include/herald/data/sensor_logger.h | 204 +++++++---- .../include/herald/data/stdout_logging_sink.h | 21 ++ .../herald/data/zephyr/zephyr_logging_sink.h | 31 ++ herald/include/herald/engine/coordinator.h | 3 +- .../simple/simple_payload_data_supplier.h | 86 ++++- herald/include/herald/sensor_array.h | 3 +- herald/include/herald/zephyr_context.h | 34 +- herald/src/ble/ble_coordinator.cpp | 34 +- herald/src/ble/concrete_ble_database.cpp | 54 ++- herald/src/ble/concrete_ble_sensor.cpp | 69 ++-- .../ble/zephyr/concrete_ble_transmitter.cpp | 10 +- herald/src/context.cpp | 61 ---- herald/src/data/contact_log.cpp | 170 --------- herald/src/data/stdout_logging_sink.cpp | 33 ++ .../src/data/zephyr/zephyr_logging_sink.cpp | 35 ++ herald/src/engine/coordinator.cpp | 36 +- .../simple/simple_payload_data_supplier.cpp | 345 +++++++++--------- herald/src/sensor_array.cpp | 51 ++- herald/src/zephyr_context.cpp | 155 ++------ 35 files changed, 1148 insertions(+), 1028 deletions(-) create mode 100644 herald-tests/test-templates.h create mode 100644 herald/include/herald/data/stdout_logging_sink.h create mode 100644 herald/include/herald/data/zephyr/zephyr_logging_sink.h delete mode 100644 herald/src/context.cpp delete mode 100644 herald/src/data/contact_log.cpp create mode 100644 herald/src/data/stdout_logging_sink.cpp create mode 100644 herald/src/data/zephyr/zephyr_logging_sink.cpp diff --git a/herald-tests/CMakeLists.txt b/herald-tests/CMakeLists.txt index 6bde891..ea7f785 100644 --- a/herald-tests/CMakeLists.txt +++ b/herald-tests/CMakeLists.txt @@ -1,6 +1,9 @@ cmake_minimum_required(VERSION 3.12) add_executable(herald-tests + # Test templates + test-templates.h + # base data types datatypes-tests.cpp datetime-tests.cpp @@ -37,9 +40,9 @@ add_executable(herald-tests include_directories(${herald_SOURCE_DIR}) -set(THREADS_PREFER_PTHREAD_FLAG ON) -find_package(Threads REQUIRED) +# set(THREADS_PREFER_PTHREAD_FLAG ON) +# find_package(Threads REQUIRED) -target_link_libraries(herald-tests PRIVATE herald Threads::Threads) +target_link_libraries(herald-tests PRIVATE herald) # Threads::Threads target_compile_features(herald-tests PRIVATE cxx_std_17) diff --git a/herald-tests/analysissensor-tests.cpp b/herald-tests/analysissensor-tests.cpp index c6ebca6..84d686a 100644 --- a/herald-tests/analysissensor-tests.cpp +++ b/herald-tests/analysissensor-tests.cpp @@ -2,6 +2,8 @@ // SPDX-License-Identifier: Apache-2.0 // +#include "test-templates.h" + #include "catch.hpp" #include "herald/herald.h" @@ -52,37 +54,6 @@ struct DummyDistanceDelegate { SampleList,25> distances; }; -class DummyLogger : public herald::data::SensorLoggingSink { -public: - DummyLogger(std::string sub,std::string cat) : subsystem(sub), category(cat), value() {} - ~DummyLogger() = default; - - void log(herald::data::SensorLoggerLevel level, std::string message) override { - value = subsystem + "," + category + "," + message; - } - - std::string subsystem; - std::string category; - std::string value; -}; - -class DummyContext : public herald::Context { -public: - DummyContext() = default; - ~DummyContext() = default; - - std::shared_ptr getBluetoothStateManager() override { return nullptr;} - - std::shared_ptr getLoggingSink(const std::string& subsystemFor, - const std::string& categoryFor) override - { - lastLogger = std::make_shared(subsystemFor,categoryFor); - return lastLogger; - } - - std::shared_ptr lastLogger; -}; - /// [Who] As a DCT app developer /// [What] I want to link my live application data to an analysis runner easily /// [Value] So I don't have to write plumbing code for Herald itself @@ -137,9 +108,12 @@ TEST_CASE("analysissensor-rssi-basic", "[analysissensor][rssi][basic]") { TEST_CASE("analysissensor-output", "[sensorlogger][analysissensor][output]") { SECTION("analysissensor-output") { - std::shared_ptr ctx = std::make_shared(); + DummyLoggingSink dls; + DummyBluetoothStateManager dbsm; + herald::Context ctx(dls,dbsm); // default context include + using CT = typename herald::Context; //herald::data::SensorLogger logger(ctx,"testout","analysissensor"); - herald::analysis::LoggingAnalysisDelegate lad(ctx); // The subject of this test + herald::analysis::LoggingAnalysisDelegate lad(ctx); // The subject of this test std::cout << "LoggingAnalysisDelegate::RAM = " << sizeof(lad) << std::endl; @@ -155,7 +129,7 @@ TEST_CASE("analysissensor-output", "[sensorlogger][analysissensor][output]") { herald::analysis::AnalysisProviderManager apm(std::move(distanceAnalyser)); // NOTE: distanceAnalyser MOVED FROM and no longer accessible herald::analysis::AnalysisRunner< - herald::analysis::AnalysisDelegateManager>, + herald::analysis::AnalysisDelegateManager>, herald::analysis::AnalysisProviderManager, RSSI,Distance > runner(adm, apm); // just for Sample types, and their produced output (Sample) @@ -178,7 +152,7 @@ TEST_CASE("analysissensor-output", "[sensorlogger][analysissensor][output]") { REQUIRE(samples[0].taken.secondsSinceUnixEpoch() != 0); REQUIRE(samples[0].value != 0.0); - auto lastMsg = ctx->lastLogger->value; + auto lastMsg = dls.value; REQUIRE(lastMsg != ""); // must have logged something... std::cout << "Last log message: " << lastMsg << std::endl; diff --git a/herald-tests/blecoordinator-tests.cpp b/herald-tests/blecoordinator-tests.cpp index 4e1ee6b..33e41c7 100644 --- a/herald-tests/blecoordinator-tests.cpp +++ b/herald-tests/blecoordinator-tests.cpp @@ -6,13 +6,16 @@ #include #include +#include "test-templates.h" + #include "catch.hpp" #include "herald/herald.h" +template class NoOpHeraldV1ProtocolProvider : public herald::ble::HeraldProtocolV1Provider { public: - NoOpHeraldV1ProtocolProvider(std::shared_ptr context,std::shared_ptr bledb) + NoOpHeraldV1ProtocolProvider(ContextT& context,std::shared_ptr bledb) : ctx(context) HLOGGERINIT(ctx,"TESTS","NoOpProvider") {} @@ -78,8 +81,8 @@ class NoOpHeraldV1ProtocolProvider : public herald::ble::HeraldProtocolV1Provide return {}; } - std::shared_ptr ctx; - HLOGGER + ContextT& ctx; + HLOGGER(ContextT); }; /* @@ -112,14 +115,16 @@ class MockHeraldV1ProtocolProvider : public herald::ble::HeraldProtocolV1Provide TEST_CASE("blecoordinator-ctor", "[coordinator][ctor][basic]") { SECTION("blecoordinator-ctor") { - std::shared_ptr ctx = - std::make_shared(); - std::shared_ptr db = - std::make_shared(ctx); - std::shared_ptr pp = - std::make_shared(ctx,db); - std::shared_ptr coord = - std::make_shared(ctx,db,pp); + DummyLoggingSink dls; + DummyBluetoothStateManager dbsm; + herald::Context ctx(dls,dbsm); // default context include + using CT = typename herald::Context; + std::shared_ptr> db = + std::make_shared>(ctx); + std::shared_ptr> pp = + std::make_shared>(ctx,db); + std::shared_ptr> coord = + std::make_shared>(ctx,db,pp); std::vector provided = coord->connectionsProvided(); REQUIRE(provided.size() == 1); // Herald BLE protocol @@ -139,14 +144,16 @@ TEST_CASE("blecoordinator-ctor", "[coordinator][ctor][basic]") { TEST_CASE("blecoordinator-unseen-device", "[coordinator][unseen-device][basic]") { SECTION("blecoordinator-unseen-device") { - std::shared_ptr ctx = - std::make_shared(); - std::shared_ptr db = - std::make_shared(ctx); - std::shared_ptr pp = - std::make_shared(ctx,db); - std::shared_ptr coord = - std::make_shared(ctx,db,pp); + DummyLoggingSink dls; + DummyBluetoothStateManager dbsm; + herald::Context ctx(dls,dbsm); // default context include + using CT = typename herald::Context; + std::shared_ptr> db = + std::make_shared>(ctx); + std::shared_ptr> pp = + std::make_shared>(ctx,db); + std::shared_ptr> coord = + std::make_shared>(ctx,db,pp); herald::datatype::Data devMac1(std::byte(0x1d),6); herald::datatype::TargetIdentifier device1(devMac1); @@ -172,14 +179,16 @@ TEST_CASE("blecoordinator-unseen-device", "[coordinator][unseen-device][basic]") TEST_CASE("blecoordinator-android-no-id", "[coordinator][android-no-id][basic]") { SECTION("blecoordinator-android-no-id") { - std::shared_ptr ctx = - std::make_shared(); - std::shared_ptr db = - std::make_shared(ctx); - std::shared_ptr pp = - std::make_shared(ctx,db); - std::shared_ptr coord = - std::make_shared(ctx,db,pp); + DummyLoggingSink dls; + DummyBluetoothStateManager dbsm; + herald::Context ctx(dls,dbsm); // default context include + using CT = typename herald::Context; + std::shared_ptr> db = + std::make_shared>(ctx); + std::shared_ptr> pp = + std::make_shared>(ctx,db); + std::shared_ptr> coord = + std::make_shared>(ctx,db,pp); herald::datatype::Data devMac1(std::byte(0x1d),6); herald::datatype::TargetIdentifier device1(devMac1); @@ -210,14 +219,16 @@ TEST_CASE("blecoordinator-android-no-id", "[coordinator][android-no-id][basic]") TEST_CASE("blecoordinator-two-mixed-no-id", "[coordinator][two-mixed-no-id][basic]") { SECTION("blecoordinator-two-mixed-no-id") { - std::shared_ptr ctx = - std::make_shared(); - std::shared_ptr db = - std::make_shared(ctx); - std::shared_ptr pp = - std::make_shared(ctx,db); - std::shared_ptr coord = - std::make_shared(ctx,db,pp); + DummyLoggingSink dls; + DummyBluetoothStateManager dbsm; + herald::Context ctx(dls,dbsm); // default context include + using CT = typename herald::Context; + std::shared_ptr> db = + std::make_shared>(ctx); + std::shared_ptr> pp = + std::make_shared>(ctx,db); + std::shared_ptr> coord = + std::make_shared>(ctx,db,pp); herald::datatype::Data devMac1(std::byte(0x1d),6); herald::datatype::TargetIdentifier device1(devMac1); @@ -254,14 +265,16 @@ TEST_CASE("blecoordinator-two-mixed-no-id", "[coordinator][two-mixed-no-id][basi TEST_CASE("blecoordinator-got-os-and-id", "[coordinator][got-os-and-id][basic]") { SECTION("blecoordinator-got-os-and-id") { - std::shared_ptr ctx = - std::make_shared(); - std::shared_ptr db = - std::make_shared(ctx); - std::shared_ptr pp = - std::make_shared(ctx,db); - std::shared_ptr coord = - std::make_shared(ctx,db,pp); + DummyLoggingSink dls; + DummyBluetoothStateManager dbsm; + herald::Context ctx(dls,dbsm); // default context include + using CT = typename herald::Context; + std::shared_ptr> db = + std::make_shared>(ctx); + std::shared_ptr> pp = + std::make_shared>(ctx,db); + std::shared_ptr> coord = + std::make_shared>(ctx,db,pp); herald::datatype::Data devMac1(std::byte(0x1d),6); herald::datatype::TargetIdentifier device1(devMac1); @@ -286,14 +299,16 @@ TEST_CASE("blecoordinator-got-os-and-id", "[coordinator][got-os-and-id][basic]") TEST_CASE("blecoordinator-got-two-at-different-states", "[coordinator][got-two-at-different-states][basic]") { SECTION("blecoordinator-got-two-at-different-states") { - std::shared_ptr ctx = - std::make_shared(); - std::shared_ptr db = - std::make_shared(ctx); - std::shared_ptr pp = - std::make_shared(ctx,db); - std::shared_ptr coord = - std::make_shared(ctx,db,pp); + DummyLoggingSink dls; + DummyBluetoothStateManager dbsm; + herald::Context ctx(dls,dbsm); // default context include + using CT = typename herald::Context; + std::shared_ptr> db = + std::make_shared>(ctx); + std::shared_ptr> pp = + std::make_shared>(ctx,db); + std::shared_ptr> coord = + std::make_shared>(ctx,db,pp); herald::datatype::Data devMac1(std::byte(0x1d),6); herald::datatype::TargetIdentifier device1(devMac1); @@ -330,14 +345,16 @@ TEST_CASE("blecoordinator-got-two-at-different-states", "[coordinator][got-two-a TEST_CASE("blecoordinator-got-immediate-send-targeted", "[coordinator][got-immediate-send-targeted][basic]") { SECTION("blecoordinator-got-immediate-send-targeted") { - std::shared_ptr ctx = - std::make_shared(); - std::shared_ptr db = - std::make_shared(ctx); - std::shared_ptr pp = - std::make_shared(ctx,db); - std::shared_ptr coord = - std::make_shared(ctx,db,pp); + DummyLoggingSink dls; + DummyBluetoothStateManager dbsm; + herald::Context ctx(dls,dbsm); // default context include + using CT = typename herald::Context; + std::shared_ptr> db = + std::make_shared>(ctx); + std::shared_ptr> pp = + std::make_shared>(ctx,db); + std::shared_ptr> coord = + std::make_shared>(ctx,db,pp); herald::datatype::Data devMac1(std::byte(0x1d),6); herald::datatype::TargetIdentifier device1(devMac1); @@ -364,14 +381,16 @@ TEST_CASE("blecoordinator-got-immediate-send-targeted", "[coordinator][got-immed TEST_CASE("blecoordinator-got-three-at-different-states", "[coordinator][got-three-at-different-states][basic]") { SECTION("blecoordinator-got-three-at-different-states") { - std::shared_ptr ctx = - std::make_shared(); - std::shared_ptr db = - std::make_shared(ctx); - std::shared_ptr pp = - std::make_shared(ctx,db); - std::shared_ptr coord = - std::make_shared(ctx,db,pp); + DummyLoggingSink dls; + DummyBluetoothStateManager dbsm; + herald::Context ctx(dls,dbsm); // default context include + using CT = typename herald::Context; + std::shared_ptr> db = + std::make_shared>(ctx); + std::shared_ptr> pp = + std::make_shared>(ctx,db); + std::shared_ptr> coord = + std::make_shared>(ctx,db,pp); herald::datatype::Data devMac1(std::byte(0x1d),6); herald::datatype::TargetIdentifier device1(devMac1); diff --git a/herald-tests/bledatabase-tests.cpp b/herald-tests/bledatabase-tests.cpp index 03d413e..539ba43 100644 --- a/herald-tests/bledatabase-tests.cpp +++ b/herald-tests/bledatabase-tests.cpp @@ -5,6 +5,8 @@ #include #include +#include "test-templates.h" + #include "catch.hpp" #include "herald/herald.h" @@ -42,10 +44,12 @@ class DummyBLEDBDelegate : public herald::ble::BLEDatabaseDelegate { TEST_CASE("ble-database-empty", "[ble][database][ctor][empty]") { SECTION("ble-database-empty") { - std::shared_ptr ctx = - std::make_shared(); - std::shared_ptr db = - std::make_shared(ctx); // enables shared_from_this + DummyLoggingSink dls; + DummyBluetoothStateManager dbsm; + herald::Context ctx(dls,dbsm); // default context include + using CT = typename herald::Context; + std::shared_ptr> db = + std::make_shared>(ctx); // enables shared_from_this REQUIRE(db->size() == 0); } @@ -53,10 +57,12 @@ TEST_CASE("ble-database-empty", "[ble][database][ctor][empty]") { TEST_CASE("ble-database-callback-verify", "[ble][database][callback][verify]") { SECTION("ble-callback-verify") { - std::shared_ptr ctx = - std::make_shared(); - std::shared_ptr db = - std::make_shared(ctx); // enables shared_from_this + DummyLoggingSink dls; + DummyBluetoothStateManager dbsm; + herald::Context ctx(dls,dbsm); // default context include + using CT = typename herald::Context; + std::shared_ptr> db = + std::make_shared>(ctx); // enables shared_from_this std::shared_ptr delegate = std::make_shared(); db->add(delegate); diff --git a/herald-tests/coordinator-tests.cpp b/herald-tests/coordinator-tests.cpp index 6ff02d6..10008ee 100644 --- a/herald-tests/coordinator-tests.cpp +++ b/herald-tests/coordinator-tests.cpp @@ -7,6 +7,8 @@ #include #include +#include "test-templates.h" + #include "catch.hpp" #include "herald/herald.h" @@ -16,9 +18,10 @@ * to test the iteration functionality of the core Coordinator class */ +template class MockSensor : public herald::ble::Sensor { public: - MockSensor(std::shared_ptr provider) : cp(provider) {} + MockSensor(std::shared_ptr> provider) : cp(provider) {} ~MockSensor() = default; @@ -31,12 +34,13 @@ class MockSensor : public herald::ble::Sensor { return std::optional>(cp); } - std::shared_ptr cp; + std::shared_ptr> cp; }; +template class NoOpHeraldV1ProtocolProvider : public herald::ble::HeraldProtocolV1Provider { public: - NoOpHeraldV1ProtocolProvider(std::shared_ptr context,std::shared_ptr bledb) + NoOpHeraldV1ProtocolProvider(ContextT& context,std::shared_ptr bledb) : ctx(context) HLOGGERINIT(ctx,"TESTS","NoOpProvider") {} @@ -103,13 +107,14 @@ class NoOpHeraldV1ProtocolProvider : public herald::ble::HeraldProtocolV1Provide return {}; } - std::shared_ptr ctx; - HLOGGER + ContextT& ctx; + HLOGGER(ContextT); }; +template class MockHeraldV1ProtocolProvider : public herald::ble::HeraldProtocolV1Provider { public: - MockHeraldV1ProtocolProvider(std::shared_ptr context,std::shared_ptr bledb) + MockHeraldV1ProtocolProvider(ContextT& context,std::shared_ptr bledb) : ctx(context), db(bledb), hasIdentifiedOs(false), lastDeviceOS(), hasReadPayload(false), lastDevicePayload(), hasImmediateSend(false), lastImmediateSend(), hasImmediateSendAll(false), lastImmediateSendAll() HLOGGERINIT(ctx,"TESTS","MockHeraldV1ProtocolProvider") @@ -211,7 +216,7 @@ class MockHeraldV1ProtocolProvider : public herald::ble::HeraldProtocolV1Provide return {}; } - std::shared_ptr ctx; + ContextT& ctx; std::shared_ptr db; bool hasIdentifiedOs; @@ -226,7 +231,7 @@ class MockHeraldV1ProtocolProvider : public herald::ble::HeraldProtocolV1Provide bool hasImmediateSendAll; std::optional lastImmediateSendAll; - HLOGGER; + HLOGGER(ContextT); }; @@ -234,21 +239,23 @@ class MockHeraldV1ProtocolProvider : public herald::ble::HeraldProtocolV1Provide TEST_CASE("coordinator-complex-iterations", "[coordinator][iterations][complex]") { // create our BLE coordinator - std::shared_ptr ctx = - std::make_shared(); - std::shared_ptr db = - std::make_shared(ctx); - std::shared_ptr pp = - std::make_shared(ctx,db); - std::shared_ptr coord = - std::make_shared(ctx,db,pp); + DummyLoggingSink dls; + DummyBluetoothStateManager dbsm; + herald::Context ctx(dls,dbsm); // default context include + using CT = typename herald::Context; + std::shared_ptr> db = + std::make_shared>(ctx); + std::shared_ptr> pp = + std::make_shared>(ctx,db); + std::shared_ptr> coord = + std::make_shared>(ctx,db,pp); // Mock Sensor - std::shared_ptr mockSensor = std::make_shared(coord); + std::shared_ptr> mockSensor = std::make_shared>(coord); // register ble coordinator - std::shared_ptr c = - std::make_shared(ctx); + std::shared_ptr> c = + std::make_shared>(ctx); c->add(mockSensor); // registers the BLE coordinator c->start(); diff --git a/herald-tests/errorcontactlog-tests.cpp b/herald-tests/errorcontactlog-tests.cpp index 61da9e5..2a368b5 100644 --- a/herald-tests/errorcontactlog-tests.cpp +++ b/herald-tests/errorcontactlog-tests.cpp @@ -2,6 +2,8 @@ // SPDX-License-Identifier: Apache-2.0 // +#include "test-templates.h" + #include "catch.hpp" #include @@ -9,48 +11,60 @@ #include "herald/herald.h" -class DummyLogger : public herald::data::SensorLoggingSink { -public: - DummyLogger(std::string sub,std::string cat) : subsystem(sub), category(cat), value() {} - ~DummyLogger() = default; +// class LoggingSink { +// public: +// LoggingSink() : subsystem(), category(), value() {} +// ~LoggingSink() = default; - void log(herald::data::SensorLoggerLevel level, std::string message) override { - // std::cout << "DummyLogger::log" << std::endl; - value = subsystem + "," + category + "," + message; - } +// void log(const std::string& sub,const std::string& cat,herald::data::SensorLoggerLevel level, std::string message) { +// // std::cout << "DummyLogger::log" << std::endl; +// value = sub + "," + cat + "," + message; +// subsystem = sub; +// category = cat; +// } - std::string subsystem; - std::string category; - std::string value; -}; +// std::string subsystem; +// std::string category; +// std::string value; +// }; -class DummyContext : public herald::Context { -public: - DummyContext() = default; - ~DummyContext() = default; +// class DummyContext : public herald::BluetoothStateManager { +// public: +// DummyContext() : sink() {}; +// ~DummyContext() = default; - std::shared_ptr getBluetoothStateManager() override { return nullptr;} +// herald::ble::BluetoothStateManager& getBluetoothStateManager() { +// return *this; +// } - std::shared_ptr getLoggingSink(const std::string& subsystemFor, - const std::string& categoryFor) override - { - // std::cout << "DummyContext::getLoggingSink" << std::endl; - lastLogger = std::make_shared(subsystemFor,categoryFor); - return lastLogger; - } +// LoggingSink& getLoggingSink() +// { +// return sink; +// } + +// void add(std::shared_ptr delegate) override { +// // ignore +// } + +// herald::datatype::BluetoothState state() override { +// return herald::datatype::BluetoothState::poweredOn; +// } - std::shared_ptr lastLogger; -}; +// LoggingSink sink; +// }; TEST_CASE("errorcontactlogger-output-dbg", "[errorcontactlogger][output]") { SECTION("errorcontactlogger-output-dbg") { - std::shared_ptr ctx = std::make_shared(); + DummyLoggingSink dls; + DummyBluetoothStateManager dbsm; + herald::Context ctx(dls,dbsm); // default context include + using CT = typename herald::Context; // Create contact logger std::shared_ptr pdf = std::make_shared(); - std::shared_ptr contacts = - std::make_shared(ctx, pdf); + std::shared_ptr> contacts = + std::make_shared>(ctx, pdf); // test each method to check for output // didDetect @@ -65,6 +79,6 @@ TEST_CASE("errorcontactlogger-output-dbg", "[errorcontactlogger][output]") { // - Was due to SHARED LIBRARIES on Windows - need to add HERALD_LOG_LEVEL to // TOP LEVEL CMakeLists.txt, not just herald-tests project. - REQUIRE(ctx->lastLogger->value.size() > 0); + REQUIRE(dls.value.size() > 0); } } \ No newline at end of file diff --git a/herald-tests/sensorlogger-tests.cpp b/herald-tests/sensorlogger-tests.cpp index a4804be..fcf3e4e 100644 --- a/herald-tests/sensorlogger-tests.cpp +++ b/herald-tests/sensorlogger-tests.cpp @@ -2,251 +2,245 @@ // SPDX-License-Identifier: Apache-2.0 // +#include "test-templates.h" + #include "catch.hpp" #include #include "herald/herald.h" -class DummyLogger : public herald::data::SensorLoggingSink { -public: - DummyLogger(std::string sub,std::string cat) : subsystem(sub), category(cat), value() {} - ~DummyLogger() = default; - - void log(herald::data::SensorLoggerLevel level, std::string message) override { - value = subsystem + "," + category + "," + message; - } - - std::string subsystem; - std::string category; - std::string value; -}; - -class DummyContext : public herald::Context { -public: - DummyContext() = default; - ~DummyContext() = default; - - std::shared_ptr getBluetoothStateManager() override { return nullptr;} - - std::shared_ptr getLoggingSink(const std::string& subsystemFor, - const std::string& categoryFor) override - { - lastLogger = std::make_shared(subsystemFor,categoryFor); - return lastLogger; - } - - std::shared_ptr lastLogger; -}; - TEST_CASE("sensorlogger-output-dbg", "[sensorlogger][output]") { SECTION("sensorlogger-output-dbg") { - std::shared_ptr ctx = std::make_shared(); - herald::data::SensorLogger logger(ctx,"testout","mytest"); + DummyLoggingSink dls; + DummyBluetoothStateManager dbsm; + herald::Context ctx(dls,dbsm); // default context include + using CT = typename herald::Context; + herald::data::SensorLogger logger(ctx.getLoggingSink(),"testout","mytest"); HTDBG("Simple string"); std::string r("testout,mytest,Simple string"); - REQUIRE(strcmp(r.c_str(),ctx->lastLogger->value.c_str()) == 0); + REQUIRE(strcmp(r.c_str(),dls.value.c_str()) == 0); HTDBG("There are {} strings","two"); r = "testout,mytest,There are two strings"; - REQUIRE(strcmp(r.c_str(),ctx->lastLogger->value.c_str()) == 0); + REQUIRE(strcmp(r.c_str(),dls.value.c_str()) == 0); HTDBG("There are {} strings",2); r = "testout,mytest,There are 2 strings"; - REQUIRE(strcmp(r.c_str(),ctx->lastLogger->value.c_str()) == 0); + REQUIRE(strcmp(r.c_str(),dls.value.c_str()) == 0); const char* cc = "some const char"; HTDBG("There are {} const chars",cc); r = "testout,mytest,There are some const char const chars"; - REQUIRE(strcmp(r.c_str(),ctx->lastLogger->value.c_str()) == 0); + REQUIRE(strcmp(r.c_str(),dls.value.c_str()) == 0); HTDBG("There are two params 1: {} and 2: {} and some more text", 15, 45); r = "testout,mytest,There are two params 1: 15 and 2: 45 and some more text"; - REQUIRE(strcmp(r.c_str(),ctx->lastLogger->value.c_str()) == 0); + REQUIRE(strcmp(r.c_str(),dls.value.c_str()) == 0); HTDBG("There are two params 1: {} and 2: {} and some more text {} <- but this is blank", 15, 45); r = "testout,mytest,There are two params 1: 15 and 2: 45 and some more text <- but this is blank"; - REQUIRE(strcmp(r.c_str(),ctx->lastLogger->value.c_str()) == 0); + REQUIRE(strcmp(r.c_str(),dls.value.c_str()) == 0); HTDBG("Too few {} parameters",15,45); r = "testout,mytest,Too few 15 parameters"; - REQUIRE(strcmp(r.c_str(),ctx->lastLogger->value.c_str()) == 0); + REQUIRE(strcmp(r.c_str(),dls.value.c_str()) == 0); } } TEST_CASE("sensorlogger-output-log", "[sensorlogger][output]") { SECTION("sensorlogger-output-log") { - std::shared_ptr ctx = std::make_shared(); - herald::data::SensorLogger logger(ctx,"testout","mytest"); + DummyLoggingSink dls; + DummyBluetoothStateManager dbsm; + herald::Context ctx(dls,dbsm); // default context include + using CT = typename herald::Context; + herald::data::SensorLogger logger(ctx.getLoggingSink(),"testout","mytest"); HTLOG("Simple string"); std::string r("testout,mytest,Simple string"); - REQUIRE(strcmp(r.c_str(),ctx->lastLogger->value.c_str()) == 0); + REQUIRE(strcmp(r.c_str(),dls.value.c_str()) == 0); HTLOG("There are {} strings","two"); r = "testout,mytest,There are two strings"; - REQUIRE(strcmp(r.c_str(),ctx->lastLogger->value.c_str()) == 0); + REQUIRE(strcmp(r.c_str(),dls.value.c_str()) == 0); HTLOG("There are {} strings",2); r = "testout,mytest,There are 2 strings"; - REQUIRE(strcmp(r.c_str(),ctx->lastLogger->value.c_str()) == 0); + REQUIRE(strcmp(r.c_str(),dls.value.c_str()) == 0); HTLOG("There are two params 1: {} and 2: {} and some more text", 15, 45); r = "testout,mytest,There are two params 1: 15 and 2: 45 and some more text"; - REQUIRE(strcmp(r.c_str(),ctx->lastLogger->value.c_str()) == 0); + REQUIRE(strcmp(r.c_str(),dls.value.c_str()) == 0); HTLOG("There are two params 1: {} and 2: {} and some more text {} <- but this is blank", 15, 45); r = "testout,mytest,There are two params 1: 15 and 2: 45 and some more text <- but this is blank"; - REQUIRE(strcmp(r.c_str(),ctx->lastLogger->value.c_str()) == 0); + REQUIRE(strcmp(r.c_str(),dls.value.c_str()) == 0); HTLOG("Too few {} parameters",15,45); r = "testout,mytest,Too few 15 parameters"; - REQUIRE(strcmp(r.c_str(),ctx->lastLogger->value.c_str()) == 0); + REQUIRE(strcmp(r.c_str(),dls.value.c_str()) == 0); } } TEST_CASE("sensorlogger-output-fault", "[sensorlogger][output]") { SECTION("sensorlogger-output-fault") { - std::shared_ptr ctx = std::make_shared(); - herald::data::SensorLogger logger(ctx,"testout","mytest"); + DummyLoggingSink dls; + DummyBluetoothStateManager dbsm; + herald::Context ctx(dls,dbsm); // default context include + using CT = typename herald::Context; + herald::data::SensorLogger logger(ctx.getLoggingSink(),"testout","mytest"); HTERR("Simple string"); std::string r("testout,mytest,Simple string"); - REQUIRE(strcmp(r.c_str(),ctx->lastLogger->value.c_str()) == 0); + REQUIRE(strcmp(r.c_str(),dls.value.c_str()) == 0); HTERR("There are {} strings","two"); r = "testout,mytest,There are two strings"; - REQUIRE(strcmp(r.c_str(),ctx->lastLogger->value.c_str()) == 0); + REQUIRE(strcmp(r.c_str(),dls.value.c_str()) == 0); HTERR("There are {} strings",2); r = "testout,mytest,There are 2 strings"; - REQUIRE(strcmp(r.c_str(),ctx->lastLogger->value.c_str()) == 0); + REQUIRE(strcmp(r.c_str(),dls.value.c_str()) == 0); HTERR("There are two params 1: {} and 2: {} and some more text", 15, 45); r = "testout,mytest,There are two params 1: 15 and 2: 45 and some more text"; - REQUIRE(strcmp(r.c_str(),ctx->lastLogger->value.c_str()) == 0); + REQUIRE(strcmp(r.c_str(),dls.value.c_str()) == 0); HTERR("There are two params 1: {} and 2: {} and some more text {} <- but this is blank", 15, 45); r = "testout,mytest,There are two params 1: 15 and 2: 45 and some more text <- but this is blank"; - REQUIRE(strcmp(r.c_str(),ctx->lastLogger->value.c_str()) == 0); + REQUIRE(strcmp(r.c_str(),dls.value.c_str()) == 0); HTERR("Too few {} parameters",15,45); r = "testout,mytest,Too few 15 parameters"; - REQUIRE(strcmp(r.c_str(),ctx->lastLogger->value.c_str()) == 0); + REQUIRE(strcmp(r.c_str(),dls.value.c_str()) == 0); } } TEST_CASE("sensorlogger-output-intrinsic", "[sensorlogger][output]") { SECTION("sensorlogger-output-intrinsic") { - std::shared_ptr ctx = std::make_shared(); - herald::data::SensorLogger logger(ctx,"testout","mytest"); + DummyLoggingSink dls; + DummyBluetoothStateManager dbsm; + herald::Context ctx(dls,dbsm); // default context include + using CT = typename herald::Context; + herald::data::SensorLogger logger(ctx.getLoggingSink(),"testout","mytest"); int i = 37; HTLOG("Intrinsic {} type",i); std::string r("testout,mytest,Intrinsic 37 type"); - REQUIRE(strcmp(r.c_str(),ctx->lastLogger->value.c_str()) == 0); + REQUIRE(strcmp(r.c_str(),dls.value.c_str()) == 0); std::uint8_t ui8 = 39; HTLOG("Intrinsic {} type",ui8); r = "testout,mytest,Intrinsic 39 type"; - REQUIRE(strcmp(r.c_str(),ctx->lastLogger->value.c_str()) == 0); + REQUIRE(strcmp(r.c_str(),dls.value.c_str()) == 0); std::size_t st = 128; HTLOG("Intrinsic {} type",st); r = "testout,mytest,Intrinsic 128 type"; - REQUIRE(strcmp(r.c_str(),ctx->lastLogger->value.c_str()) == 0); + REQUIRE(strcmp(r.c_str(),dls.value.c_str()) == 0); std::uint16_t ui16 = 3737; HTLOG("Intrinsic {} type",ui16); r = "testout,mytest,Intrinsic 3737 type"; - REQUIRE(strcmp(r.c_str(),ctx->lastLogger->value.c_str()) == 0); + REQUIRE(strcmp(r.c_str(),dls.value.c_str()) == 0); std::uint32_t ui32 = 373737; HTLOG("Intrinsic {} type",ui32); r = "testout,mytest,Intrinsic 373737 type"; - REQUIRE(strcmp(r.c_str(),ctx->lastLogger->value.c_str()) == 0); + REQUIRE(strcmp(r.c_str(),dls.value.c_str()) == 0); std::uint32_t ui64 = 3737373737; HTLOG("Intrinsic {} type",ui64); r = "testout,mytest,Intrinsic 3737373737 type"; - REQUIRE(strcmp(r.c_str(),ctx->lastLogger->value.c_str()) == 0); + REQUIRE(strcmp(r.c_str(),dls.value.c_str()) == 0); std::int8_t i8 = -39; HTLOG("Intrinsic {} type",i8); r = "testout,mytest,Intrinsic -39 type"; - REQUIRE(strcmp(r.c_str(),ctx->lastLogger->value.c_str()) == 0); + REQUIRE(strcmp(r.c_str(),dls.value.c_str()) == 0); std::int16_t i16 = -3737; HTLOG("Intrinsic {} type",i16); r = "testout,mytest,Intrinsic -3737 type"; - REQUIRE(strcmp(r.c_str(),ctx->lastLogger->value.c_str()) == 0); + REQUIRE(strcmp(r.c_str(),dls.value.c_str()) == 0); std::int32_t i32 = -373737; HTLOG("Intrinsic {} type",i32); r = "testout,mytest,Intrinsic -373737 type"; - REQUIRE(strcmp(r.c_str(),ctx->lastLogger->value.c_str()) == 0); + REQUIRE(strcmp(r.c_str(),dls.value.c_str()) == 0); std::int32_t i64 = -37373737; HTLOG("Intrinsic {} type",i64); r = "testout,mytest,Intrinsic -37373737 type"; - REQUIRE(strcmp(r.c_str(),ctx->lastLogger->value.c_str()) == 0); + REQUIRE(strcmp(r.c_str(),dls.value.c_str()) == 0); } } TEST_CASE("sensorlogger-bug-negativesuccess", "[sensorlogger][output][bug]") { SECTION("sensorlogger-bug-negativesuccess") { - std::shared_ptr ctx = std::make_shared(); - herald::data::SensorLogger logger(ctx,"testout","mytest"); + DummyLoggingSink dls; + DummyBluetoothStateManager dbsm; + herald::Context ctx(dls,dbsm); // default context include + using CT = typename herald::Context; + herald::data::SensorLogger logger(ctx.getLoggingSink(),"testout","mytest"); int success = -22; HTDBG(" - Issue connecting: {}",success); std::string r("testout,mytest, - Issue connecting: -22"); - REQUIRE(strcmp(r.c_str(),ctx->lastLogger->value.c_str()) == 0); + REQUIRE(strcmp(r.c_str(),dls.value.c_str()) == 0); } } TEST_CASE("sensorlogger-bug-targetidatend", "[sensorlogger][output][bug]") { SECTION("sensorlogger-bug-targetidatend") { - std::shared_ptr ctx = std::make_shared(); - herald::data::SensorLogger logger(ctx,"testout","mytest"); + DummyLoggingSink dls; + DummyBluetoothStateManager dbsm; + herald::Context ctx(dls,dbsm); // default context include + using CT = typename herald::Context; + herald::data::SensorLogger logger(ctx.getLoggingSink(),"testout","mytest"); herald::datatype::TargetIdentifier t(herald::datatype::Data(std::byte(0x09),3)); HTLOG("Complex {}",t); std::string r("testout,mytest,Complex 090909"); - REQUIRE(strcmp(r.c_str(),ctx->lastLogger->value.c_str()) == 0); + REQUIRE(strcmp(r.c_str(),dls.value.c_str()) == 0); } } TEST_CASE("sensorlogger-output-data", "[sensorlogger][output]") { SECTION("sensorlogger-output-data") { - std::shared_ptr ctx = std::make_shared(); - herald::data::SensorLogger logger(ctx,"testout","mytest"); + DummyLoggingSink dls; + DummyBluetoothStateManager dbsm; + herald::Context ctx(dls,dbsm); // default context include + using CT = typename herald::Context; + herald::data::SensorLogger logger(ctx.getLoggingSink(),"testout","mytest"); herald::datatype::Data t(std::byte(0x09),3); HTLOG("Complex {} type",t); std::string r("testout,mytest,Complex 090909 type"); - REQUIRE(strcmp(r.c_str(),ctx->lastLogger->value.c_str()) == 0); + REQUIRE(strcmp(r.c_str(),dls.value.c_str()) == 0); } } TEST_CASE("sensorlogger-output-targetidentifier", "[sensorlogger][output]") { SECTION("sensorlogger-output-targetidentifier") { - std::shared_ptr ctx = std::make_shared(); - herald::data::SensorLogger logger(ctx,"testout","mytest"); + DummyLoggingSink dls; + DummyBluetoothStateManager dbsm; + herald::Context ctx(dls,dbsm); // default context include + using CT = typename herald::Context; + herald::data::SensorLogger logger(ctx.getLoggingSink(),"testout","mytest"); herald::datatype::TargetIdentifier t(herald::datatype::Data(std::byte(0x09),3)); std::string r("testout,mytest,Complex 090909 type"); HTLOG("Complex {} type",t); - REQUIRE(strcmp(r.c_str(),ctx->lastLogger->value.c_str()) == 0); - // REQUIRE(r.compare(ctx->lastLogger->value) == 0); + REQUIRE(strcmp(r.c_str(),dls.value.c_str()) == 0); } } \ No newline at end of file diff --git a/herald-tests/simplepayload-tests.cpp b/herald-tests/simplepayload-tests.cpp index a396090..5fb45f0 100644 --- a/herald-tests/simplepayload-tests.cpp +++ b/herald-tests/simplepayload-tests.cpp @@ -2,6 +2,8 @@ // SPDX-License-Identifier: Apache-2.0 // +#include "test-templates.h" + #include "catch.hpp" #include "herald/herald.h" @@ -235,8 +237,10 @@ TEST_CASE("payload-simple-contactid", "[payload][simple][contactid]") { TEST_CASE("payload-simple-basic", "[payload][simple][basic]") { SECTION("payload-simple-basic") { - std::shared_ptr ctx = - std::make_shared(); + DummyLoggingSink dls; + DummyBluetoothStateManager dbsm; + herald::Context ctx(dls,dbsm); // default context include + using CT = typename herald::Context; std::uint16_t country = 826; std::uint16_t state = 4; herald::payload::simple::K k; @@ -272,8 +276,10 @@ TEST_CASE("payload-simple-basic", "[payload][simple][basic]") { TEST_CASE("payload-simple-payloadbounds", "[payload][simple][payloadbounds]") { SECTION("payload-simple-payloadbounds") { - std::shared_ptr ctx = - std::make_shared(); + DummyLoggingSink dls; + DummyBluetoothStateManager dbsm; + herald::Context ctx(dls,dbsm); // default context include + using CT = typename herald::Context; std::uint16_t country = 826; std::uint16_t state = 4; herald::payload::simple::K k; diff --git a/herald-tests/test-templates.h b/herald-tests/test-templates.h new file mode 100644 index 0000000..7a93070 --- /dev/null +++ b/herald-tests/test-templates.h @@ -0,0 +1,40 @@ +// Copyright 2021 Herald Project Contributors +// SPDX-License-Identifier: Apache-2.0 +// + +#ifndef HERALD_TESTS_TEMPLATES_H +#define HERALD_TESTS_TEMPLATES_H + +#include "herald/herald.h" + +struct DummyLoggingSink { + DummyLoggingSink() : subsystem(), category(), value() {} + ~DummyLoggingSink() = default; + + void log(const std::string& sub,const std::string& cat,herald::data::SensorLoggerLevel level, std::string message) { + // std::cout << "DummyLogger::log" << std::endl; + value = sub + "," + cat + "," + message; + subsystem = sub; + category = cat; + } + + std::string subsystem; + std::string category; + std::string value; +}; + +class DummyBluetoothStateManager : public herald::ble::BluetoothStateManager { +public: + DummyBluetoothStateManager() = default; + ~DummyBluetoothStateManager() = default; + + void add(std::shared_ptr delegate) override { + ; // no op + } + + herald::ble::BluetoothState state() override { + return herald::ble::BluetoothState::poweredOn; + } +}; + +#endif \ No newline at end of file diff --git a/herald-wearable/CMakeLists.txt b/herald-wearable/CMakeLists.txt index 3c60ca2..a6ae725 100644 --- a/herald-wearable/CMakeLists.txt +++ b/herald-wearable/CMakeLists.txt @@ -91,7 +91,7 @@ include_directories(../herald/include) if(CMAKE_BUILD_TYPE MATCHES Debug) add_definitions(-DHERALD_LOG_LEVEL=4 -DCONFIG_APP_LOG_LEVEL=4) else() - add_definitions(-DHERALD_LOG_LEVEL=1 -DCONFIG_APP_LOG_LEVEL=1) + add_definitions(-DHERALD_LOG_LEVEL=0 -DCONFIG_APP_LOG_LEVEL=0) endif() #HERALD_LOG_LEVEL 4 = debug, 3 = info, 2 = warn / errors (Zephyr LOG_WARN), 1 = info contacts log only (Zephyr LOG_ERR) diff --git a/herald/herald.cmake b/herald/herald.cmake index d486816..63a647e 100644 --- a/herald/herald.cmake +++ b/herald/herald.cmake @@ -37,6 +37,7 @@ set(HERALD_HEADERS ${HERALD_BASE}/include/herald/data/contact_log.h ${HERALD_BASE}/include/herald/data/payload_data_formatter.h ${HERALD_BASE}/include/herald/data/sensor_logger.h + ${HERALD_BASE}/include/herald/data/stdout_logging_sink.h ${HERALD_BASE}/include/herald/datatype/base64_string.h ${HERALD_BASE}/include/herald/datatype/bluetooth_state.h ${HERALD_BASE}/include/herald/datatype/data.h @@ -78,7 +79,11 @@ set(HERALD_HEADERS ) set(HERALD_HEADERS_ZEPHYR + ${HERALD_BASE}/include/herald/data/zephyr/zephyr_logging_sink.h ${HERALD_BASE}/include/herald/zephyr_context.h +) +set(HERALD_HEADERS_WINDOWS + ) set(HERALD_SOURCES ${HERALD_BASE}/src/ble/ble_mac_address.cpp @@ -90,8 +95,8 @@ set(HERALD_SOURCES ${HERALD_BASE}/src/ble/filter/ble_advert_parser.cpp ${HERALD_BASE}/src/ble/filter/ble_advert_types.cpp ${HERALD_BASE}/src/data/concrete_payload_data_formatter.cpp - ${HERALD_BASE}/src/data/contact_log.cpp ${HERALD_BASE}/src/data/sensor_logger.cpp + ${HERALD_BASE}/src/data/stdout_logging_sink.cpp ${HERALD_BASE}/src/datatype/base64_string.cpp ${HERALD_BASE}/src/datatype/data.cpp ${HERALD_BASE}/src/datatype/date.cpp @@ -115,11 +120,12 @@ set(HERALD_SOURCES ${HERALD_BASE}/src/payload/simple/simple_payload_data_supplier.cpp ${HERALD_BASE}/src/payload/extended/extended_data.cpp ${HERALD_BASE}/src/default_sensor_delegate.cpp - ${HERALD_BASE}/src/context.cpp + #${HERALD_BASE}/src/context.cpp ${HERALD_BASE}/src/sensor_array.cpp ) set(HERALD_SOURCES_ZEPHYR ${HERALD_BASE}/src/ble/zephyr/concrete_ble_transmitter.cpp + ${HERALD_BASE}/src/data/zephyr/zephyr_logging_sink.cpp ${HERALD_BASE}/src/zephyr_context.cpp ) set(HERALD_SOURCES_MBEDTLS diff --git a/herald/include/herald/analysis/logging_analysis_delegate.h b/herald/include/herald/analysis/logging_analysis_delegate.h index e893493..987abcb 100644 --- a/herald/include/herald/analysis/logging_analysis_delegate.h +++ b/herald/include/herald/analysis/logging_analysis_delegate.h @@ -17,7 +17,7 @@ namespace analysis { using namespace sampling; /// \brief Logs any given type of sample to the Herald logging subsystem as a Debug message -template +template struct LoggingAnalysisDelegate { using value_type = ValT; @@ -28,7 +28,7 @@ struct LoggingAnalysisDelegate { { } - LoggingAnalysisDelegate(std::shared_ptr ctx) + LoggingAnalysisDelegate(ContextT& ctx) : ctx(ctx) HLOGGERINIT(ctx,"herald","LoggingAnalysisDelegate") { @@ -57,8 +57,8 @@ struct LoggingAnalysisDelegate { } private: - std::shared_ptr ctx; - HLOGGER; + ContextT& ctx; + HLOGGER(ContextT); }; diff --git a/herald/include/herald/ble/ble_concrete.h b/herald/include/herald/ble/ble_concrete.h index 488d2b0..9ac9606 100644 --- a/herald/include/herald/ble/ble_concrete.h +++ b/herald/include/herald/ble/ble_concrete.h @@ -39,9 +39,10 @@ class ConcreteBluetoothStateManager : public BluetoothStateManager, public std:: BluetoothState state() override; }; -class ConcreteBLEDatabase : public BLEDatabase, public BLEDeviceDelegate, public std::enable_shared_from_this { +template +class ConcreteBLEDatabase : public BLEDatabase, public BLEDeviceDelegate, public std::enable_shared_from_this> { public: - ConcreteBLEDatabase(std::shared_ptr context); + ConcreteBLEDatabase(ContextT& context); ConcreteBLEDatabase(const ConcreteBLEDatabase& from) = delete; ConcreteBLEDatabase(ConcreteBLEDatabase&& from) = delete; ~ConcreteBLEDatabase(); @@ -81,10 +82,11 @@ class ConcreteBLEDatabase : public BLEDatabase, public BLEDeviceDelegate, public /** * Acts as the main object to control the receiver, transmitter, and database instances */ +template class ConcreteBLESensor : public BLESensor, public BLEDatabaseDelegate, - public BluetoothStateManagerDelegate, public std::enable_shared_from_this { + public BluetoothStateManagerDelegate, public std::enable_shared_from_this> { public: - ConcreteBLESensor(std::shared_ptr ctx, std::shared_ptr bluetoothStateManager, + ConcreteBLESensor(ContextT& ctx, BluetoothStateManager& bluetoothStateManager, std::shared_ptr payloadDataSupplier); ConcreteBLESensor(const ConcreteBLESensor& from) = delete; ConcreteBLESensor(ConcreteBLESensor&& from) = delete; @@ -114,9 +116,10 @@ class ConcreteBLESensor : public BLESensor, public BLEDatabaseDelegate, std::unique_ptr mImpl; // unique as this is handled internally for all platforms by Herald }; -class ConcreteBLEReceiver : public BLEReceiver, public HeraldProtocolV1Provider, public std::enable_shared_from_this { +template +class ConcreteBLEReceiver : public BLEReceiver, public HeraldProtocolV1Provider, public std::enable_shared_from_this> { public: - ConcreteBLEReceiver(std::shared_ptr ctx, std::shared_ptr bluetoothStateManager, + ConcreteBLEReceiver(ContextT& ctx, BluetoothStateManager& bluetoothStateManager, std::shared_ptr payloadDataSupplier, std::shared_ptr bleDatabase); ConcreteBLEReceiver(const ConcreteBLEReceiver& from) = delete; ConcreteBLEReceiver(ConcreteBLEReceiver&& from) = delete; @@ -156,9 +159,10 @@ class ConcreteBLEReceiver : public BLEReceiver, public HeraldProtocolV1Provider, std::shared_ptr mImpl; // shared to allow static callbacks to be bound }; -class ConcreteBLETransmitter : public BLETransmitter, public std::enable_shared_from_this { +template +class ConcreteBLETransmitter : public BLETransmitter, public std::enable_shared_from_this> { public: - ConcreteBLETransmitter(std::shared_ptr ctx, std::shared_ptr bluetoothStateManager, + ConcreteBLETransmitter(ContextT& ctx, BluetoothStateManager& bluetoothStateManager, std::shared_ptr payloadDataSupplier, std::shared_ptr bleDatabase); ConcreteBLETransmitter(const ConcreteBLETransmitter& from) = delete; ConcreteBLETransmitter(ConcreteBLETransmitter&& from) = delete; diff --git a/herald/include/herald/ble/ble_coordinator.h b/herald/include/herald/ble/ble_coordinator.h index 67324e2..9682250 100644 --- a/herald/include/herald/ble/ble_coordinator.h +++ b/herald/include/herald/ble/ble_coordinator.h @@ -18,9 +18,10 @@ namespace herald { namespace ble { +template class HeraldProtocolBLECoordinationProvider : public CoordinationProvider { public: - HeraldProtocolBLECoordinationProvider(std::shared_ptr ctx, std::shared_ptr db, std::shared_ptr provider); + HeraldProtocolBLECoordinationProvider(ContextT& ctx, std::shared_ptr db, std::shared_ptr provider); ~HeraldProtocolBLECoordinationProvider(); // Overrides diff --git a/herald/include/herald/context.h b/herald/include/herald/context.h index 97ff8ca..06abfe5 100644 --- a/herald/include/herald/context.h +++ b/herald/include/herald/context.h @@ -5,22 +5,8 @@ #ifndef HERALD_CONTEXT_H #define HERALD_CONTEXT_H -#include "ble/bluetooth_state_manager.h" - -#include -#include -#include - namespace herald { -using namespace herald::ble; - -namespace data { -class SensorLoggingSink; // fwd decl -} - -using namespace herald::data; - /// /// \brief High level abstraction to access platform-specific implemented primitives. /// @@ -28,26 +14,75 @@ using namespace herald::data; /// doesn't map well on to C++ idioms. This class provides an extension capability /// to allow this linking. /// -class Context { -public: - Context() = default; - virtual ~Context() = default; +// class Context { +// public: +// Context() = default; +// ~Context() = default; + +// template +// SensorLogger getLogger(const std::string& subsystemFor, const std::string& categoryFor); +// // virtual std::shared_ptr getLoggingSink(const std::string& subsystemFor, const std::string& categoryFor) = 0; +// BluetoothStateManager getBluetoothStateManager(); +// }; + +//class Context; // fwd decl only here - virtual std::shared_ptr getLoggingSink(const std::string& subsystemFor, const std::string& categoryFor) = 0; - virtual std::shared_ptr getBluetoothStateManager() = 0; -}; /// /// \brief Default context that just sends logging to stdout /// -class DefaultContext : public Context { -public: - DefaultContext() = default; - ~DefaultContext() = default; +// class DefaultContext : public Context { +// public: +// DefaultContext() = default; +// ~DefaultContext() = default; + +// std::shared_ptr getBluetoothStateManager() override; + +// std::shared_ptr getLoggingSink(const std::string& subsystemFor, const std::string& categoryFor) override; +// }; + + +/// \brief Compile-time Context class, customisable via template traits. Provides generic access to OS system features. +/// +/// Covers all cross-cutting concerns methods and helpers to prevent tight coupling between components +/// Currently hard-coded to include Bluetooth relevant radio, but this should be abstracted in future to +/// compile out if Bluetooth support is not needed +template +struct Context { + using logging_sink_type = LoggingSinkT; + + Context(LoggingSinkT& sink,BluetoothStateManagerT& bsm) noexcept + : loggingSink(sink), bleStateManager(bsm) {} + Context(const Context& other) noexcept + : loggingSink(other.loggingSink), bleStateManager(other.bleStateManager) + {} + // Context(Context&& other) + // : loggingSink(std::move(other.loggingSink)), bleStateManager(std::move(other.bleStateManager)) + // {} + ~Context() = default; + + + Context& operator=(Context& other) noexcept { + loggingSink = other.loggingSink; + bleStateManager = other.bleStateManager; + return *this; + } + + // \brief Returns a reference to this OS'/runtimes implementation of the LoggingSink + LoggingSinkT& getLoggingSink() { + return loggingSink; + } - std::shared_ptr getBluetoothStateManager() override; + // \brief Returns a reference to this OS'/runtimes implemetation of the Bluetooth State Manager + BluetoothStateManagerT& getBluetoothStateManager() { + return bleStateManager; + } - std::shared_ptr getLoggingSink(const std::string& subsystemFor, const std::string& categoryFor) override; +private: + LoggingSinkT& loggingSink; + BluetoothStateManagerT& bleStateManager; }; } // end namespace diff --git a/herald/include/herald/data/contact_log.h b/herald/include/herald/data/contact_log.h index 4c5f7fa..8631cb2 100644 --- a/herald/include/herald/data/contact_log.h +++ b/herald/include/herald/data/contact_log.h @@ -8,31 +8,59 @@ #include "../sensor_delegate.h" #include "payload_data_formatter.h" #include "sensor_logger.h" +#include "../context.h" namespace herald::data { /** * Logs all contact info to STDERR to allow it to be extracted as a stream */ +template class ErrorStreamContactLogger : public SensorDelegate { public: - ErrorStreamContactLogger(std::shared_ptr context, std::shared_ptr formatter); - ~ErrorStreamContactLogger(); + ErrorStreamContactLogger(ContextT& context, std::shared_ptr formatter) + : ctx(context), + fmt(formatter) + HLOGGERINIT(ctx,"Sensor","contacts.log") + { + ; + } + ~ErrorStreamContactLogger() = default; // Sensor delegate overrides - void sensor(SensorType sensor, const TargetIdentifier& didDetect) override; - void sensor(SensorType sensor, const PayloadData& didRead, const TargetIdentifier& fromTarget) override; - void sensor(SensorType sensor, const ImmediateSendData& didReceive, const TargetIdentifier& fromTarget) override; - void sensor(SensorType sensor, const std::vector& didShare, const TargetIdentifier& fromTarget) override; - void sensor(SensorType sensor, const Proximity& didMeasure, const TargetIdentifier& fromTarget) override; + void sensor(SensorType sensor, const TargetIdentifier& didDetect) override { + HTDBG("didDetect"); + } + void sensor(SensorType sensor, const PayloadData& didRead, const TargetIdentifier& fromTarget) override {} + void sensor(SensorType sensor, const ImmediateSendData& didReceive, const TargetIdentifier& fromTarget) override {} + void sensor(SensorType sensor, const std::vector& didShare, const TargetIdentifier& fromTarget) override{} + void sensor(SensorType sensor, const Proximity& didMeasure, const TargetIdentifier& fromTarget) override {} template - void sensor(SensorType sensor, const Location& didVisit); - void sensor(SensorType sensor, const Proximity& didMeasure, const TargetIdentifier& fromTarget, const PayloadData& withPayload) override; - void sensor(SensorType sensor, const SensorState& didUpdateState) override; + void sensor(SensorType sensor, const Location& didVisit) {} + void sensor(SensorType sensor, const Proximity& didMeasure, const TargetIdentifier& fromTarget, const PayloadData& withPayload) override {} + void sensor(SensorType sensor, const SensorState& didUpdateState) override {} private: - class Impl; // fwd decl - std::unique_ptr mImpl; // PIMPL idiom + std::string csv(std::string toEscape) const noexcept { + // C++23 only: if (toEscape.contains(",") || toEscape.contains("\"") || toEscape.contains("'") || toEscape.contains("’")) { + // Pre C++23:- + if (std::string::npos != toEscape.find(",") || + std::string::npos != toEscape.find("\"") || + std::string::npos != toEscape.find("'") || + std::string::npos != toEscape.find("’")) { + return "\"" + toEscape + "\""; + } + return toEscape; + } + + std::string timestamp() const noexcept { + return Date().iso8601DateTime(); + } + + ContextT& ctx; + std::shared_ptr fmt; + + HLOGGER(ContextT); }; } diff --git a/herald/include/herald/data/sensor_logger.h b/herald/include/herald/data/sensor_logger.h index ad4e316..5e0cade 100644 --- a/herald/include/herald/data/sensor_logger.h +++ b/herald/include/herald/data/sensor_logger.h @@ -6,7 +6,6 @@ #define SENSOR_LOGGER_H #include "../datatype/bluetooth_state.h" -#include "../context.h" #include #include @@ -21,8 +20,11 @@ #ifdef HERALD_LOG_LEVEL // Defines for within Impl class definitions -#define HLOGGER herald::data::SensorLogger logger; -#define HLOGGERINIT(_ctx,_subsystem,_category) ,logger(_ctx,_subsystem,_category) +#if HERALD_LOG_LEVEL != 0 +#define HLOGGER(_ctxT) \ + herald::data::SensorLogger logger; +#define HLOGGERINIT(_ctx,_subsystem,_category) ,logger(_ctx.getLoggingSink(),_subsystem,_category) +#endif // HDBG Defines for within main class (more common) // HTDBG Defines for within Impl class @@ -63,6 +65,19 @@ #define HTERR(_msg, ...) logger.fault(_msg, ##__VA_ARGS__); #endif +#if HERALD_LOG_LEVEL == 0 + +#define HLOGGER /* No logger instance */ +#define HLOGGERINIT(...) /* No logger init */ +#define HDBG(...) /* No debug log */ +#define HERR(...) /* No error log */ +#define HLOG(...) /* No info log */ +#define HTDBG(...) /* No debug log */ +#define HTERR(...) /* No error log */ +#define HTLOG(...) /* No info log */ + +#endif + #else #define HLOGGER /* No logger instance */ @@ -77,21 +92,32 @@ #endif namespace herald { + namespace data { enum class SensorLoggerLevel : int { debug, info, fault }; -// NOTE: HEADER ONLY CLASS AS IT USES VARIABLE TEMPLATE ARGS FOR LOGGING - -class SensorLoggingSink { +/* +class LoggingSink { public: - SensorLoggingSink() = default; - virtual ~SensorLoggingSink() = default; + LoggingSink() = default; + ~LoggingSink() = default; - virtual void log(SensorLoggerLevel level, std::string message) = 0; + void log(const std::string& subsystem, const std::string& category, SensorLoggerLevel level, std::string message); }; +*/ + +// NOTE: HEADER ONLY CLASS AS IT USES VARIABLE TEMPLATE ARGS FOR LOGGING + +// class SensorLoggingSink { +// public: +// SensorLoggingSink() = default; +// virtual ~SensorLoggingSink() = default; + +// virtual void log(SensorLoggerLevel level, std::string message) = 0; +// }; namespace { @@ -113,76 +139,90 @@ namespace { } } - template - void tprintf(std::stringstream& os, const std::string& format, std::uint8_t value, Targs... Fargs) // recursive variadic function - { - std::size_t pos = 0; - for ( auto c : format ) { - if ( c == '{' ) { - os << std::uint16_t(value); - if (format.size() > pos + 1 && format.at(pos + 1) == '}') { - tprintf(os, format.substr(pos + 2), Fargs...); // recursive call - } else { - tprintf(os, format.substr(pos + 1), Fargs...); // recursive call - } - return; - } - os << c; - pos++; - } - } + // template + // void tprintf(std::stringstream& os, const std::string& format, std::uint8_t value, Targs... Fargs) // recursive variadic function + // { + // std::size_t pos = 0; + // for ( auto c : format ) { + // if ( c == '{' ) { + // os << std::uint16_t(value); + // if (format.size() > pos + 1 && format.at(pos + 1) == '}') { + // tprintf(os, format.substr(pos + 2), Fargs...); // recursive call + // } else { + // tprintf(os, format.substr(pos + 1), Fargs...); // recursive call + // } + // return; + // } + // os << c; + // pos++; + // } + // } - template - void tprintf(std::stringstream& os, const std::string& format, std::int8_t value, Targs... Fargs) // recursive variadic function - { - std::size_t pos = 0; - for ( auto c : format ) { - if ( c == '{' ) { - os << std::int16_t(value); - if (format.size() > pos + 1 && format.at(pos + 1) == '}') { - tprintf(os, format.substr(pos + 2), Fargs...); // recursive call - } else { - tprintf(os, format.substr(pos + 1), Fargs...); // recursive call - } - return; - } - os << c; - pos++; - } - } + // template + // void tprintf(std::stringstream& os, const std::string& format, std::int8_t value, Targs... Fargs) // recursive variadic function + // { + // std::size_t pos = 0; + // for ( auto c : format ) { + // if ( c == '{' ) { + // os << std::int16_t(value); + // if (format.size() > pos + 1 && format.at(pos + 1) == '}') { + // tprintf(os, format.substr(pos + 2), Fargs...); // recursive call + // } else { + // tprintf(os, format.substr(pos + 1), Fargs...); // recursive call + // } + // return; + // } + // os << c; + // pos++; + // } + // } - template - void tprintf(std::stringstream& os, const std::string& format, const std::string& value, Targs... Fargs) // recursive variadic function + // template + // void tprintf(std::stringstream& os, const std::string& format, const std::string& value, Targs... Fargs) // recursive variadic function + // { + // std::size_t pos = 0; + // for ( auto c : format ) { + // if ( c == '{' ) { + // os << value; + // if (format.size() > pos + 1 && format.at(pos + 1) == '}') { + // tprintf(os, format.substr(pos + 2), Fargs...); // recursive call + // } else { + // tprintf(os, format.substr(pos + 1), Fargs...); // recursive call + // } + // return; + // } + // os << c; + // pos++; + // } + // } + + // typename std::enable_if_t::value, std::string> + + template + void tprintf(std::stringstream& os, const std::string& format, T value) // recursive variadic function { std::size_t pos = 0; for ( auto c : format ) { if ( c == '{' ) { os << value; - if (format.size() > pos + 1 && format.at(pos + 1) == '}') { - tprintf(os, format.substr(pos + 2), Fargs...); // recursive call - } else { - tprintf(os, format.substr(pos + 1), Fargs...); // recursive call - } return; } os << c; pos++; } } - - // typename std::enable_if_t::value, std::string> - template - void tprintf(std::stringstream& os, const std::string& format, T value, Targs... Fargs) // recursive variadic function + template + void tprintf(std::stringstream& os, const std::string& format, FirstT first, SecondT second, RestT... rest) { std::size_t pos = 0; for ( auto c : format ) { if ( c == '{' ) { - os << value; + os << first; if (format.size() > pos + 1 && format.at(pos + 1) == '}') { - tprintf(os, format.substr(pos + 2), Fargs...); // recursive call + tprintf(os, format.substr(pos + 2), second, rest...); // recursive call } else { - tprintf(os, format.substr(pos + 1), Fargs...); // recursive call + tprintf(os, format.substr(pos + 1), second, rest...); // recursive call } return; } @@ -191,26 +231,46 @@ namespace { } } - // G++ deduction guide workaround - https://gcc.gnu.org/bugzilla/show_bug.cgi?id=80438 - template - void tprintf(std::stringstream& os, const std::string& format, Targs... Fargs) - { - tprintf(os, format, Fargs...); - } + // template + // void tprintf(std::stringstream& os, const std::string& format, T value, Targs... Fargs) // recursive variadic function + // { + // std::size_t pos = 0; + // for ( auto c : format ) { + // if ( c == '{' ) { + // os << value; + // if (format.size() > pos + 1 && format.at(pos + 1) == '}') { + // tprintf(os, format.substr(pos + 2), Fargs...); // recursive call + // } else { + // tprintf(os, format.substr(pos + 1), Fargs...); // recursive call + // } + // return; + // } + // os << c; + // pos++; + // } + // } + + // // G++ deduction guide workaround - https://gcc.gnu.org/bugzilla/show_bug.cgi?id=80438 + // template + // void tprintf(std::stringstream& os, const std::string& format, Targs... Fargs) + // { + // tprintf(os, format, Fargs...); + // } } +template class SensorLogger { public: - SensorLogger(const std::shared_ptr& ctx, std::string subsystem, std::string category) - : mSink(ctx->getLoggingSink(subsystem, category)), mSubsystem(subsystem), mCategory(category) + SensorLogger(LoggingSinkT& sink, std::string subsystem, std::string category) + : mSink(sink), mSubsystem(subsystem), mCategory(category) { ; } // TODO consider supporting multiple sinks in the context - E.g. USB UART and log file - ~SensorLogger() {}; // define this ourselves, but blank and not default + ~SensorLogger() = default; // use std::format to generate the string // std::format in C++20, fmt::format library before that @@ -256,12 +316,12 @@ class SensorLogger { private: inline void log(SensorLoggerLevel lvl, std::string msg) { - mSink->log(lvl, msg); + mSink.log(mSubsystem, mCategory, lvl, msg); } - std::shared_ptr mSink; - std::string mSubsystem; - std::string mCategory; + LoggingSinkT& mSink; + const std::string mSubsystem; + const std::string mCategory; }; } // end namespace diff --git a/herald/include/herald/data/stdout_logging_sink.h b/herald/include/herald/data/stdout_logging_sink.h new file mode 100644 index 0000000..8efefd3 --- /dev/null +++ b/herald/include/herald/data/stdout_logging_sink.h @@ -0,0 +1,21 @@ +// Copyright 2020-2021 Herald Project Contributors +// SPDX-License-Identifier: Apache-2.0 +// + +#ifndef HERALD_STDOUT_LOGGING_SINK +#define HERALD_STDOUT_LOGGING_SINK + +#include "herald/data/sensor_logger.h" + +namespace herald::data { + +struct StdOutLoggingSink { + StdOutLoggingSink(); + ~StdOutLoggingSink(); + + void log(const std::string& subsystem, const std::string& category, SensorLoggerLevel level, std::string message); +}; + +} + +#endif \ No newline at end of file diff --git a/herald/include/herald/data/zephyr/zephyr_logging_sink.h b/herald/include/herald/data/zephyr/zephyr_logging_sink.h new file mode 100644 index 0000000..f6f7f83 --- /dev/null +++ b/herald/include/herald/data/zephyr/zephyr_logging_sink.h @@ -0,0 +1,31 @@ +// Copyright 2020-2021 Herald Project Contributors +// SPDX-License-Identifier: Apache-2.0 +// + +#ifndef ZEPHYR_LOGGING_SINK_H +#define ZEPHYR_LOGGING_SINK_H + +#include "herald/data/sensor_logger.h" + +// NOTE: Link Herald to the Zephyr logging system +// Set HERALD_LOG_LEVEL=4 for debug in CMake using add_definitions(-DHERALD_LOG_LEVEL=4 ) +// Defaults to 0 (OFF) - see zephyr_context.h +#include + +namespace herald { + +// THE BELOW IS DONE IN EXACTLY ONE HERALD FILE +LOG_MODULE_REGISTER(heraldlogger, HERALD_LOG_LEVEL); + +namespace data { + +class ZephyrLoggingSink { +public: + log(const std::string& subsystem,const std::string& category, + SensorLoggerLevel level, const std::string& message); +}; + +} // end namespace +} // end namespace + +#endif \ No newline at end of file diff --git a/herald/include/herald/engine/coordinator.h b/herald/include/herald/engine/coordinator.h index c2a60ee..dbccb23 100644 --- a/herald/include/herald/engine/coordinator.h +++ b/herald/include/herald/engine/coordinator.h @@ -30,10 +30,11 @@ namespace engine { /// /// - SensorArray /// +template class Coordinator { public: /// Default constructor. Receives a configured platform-specific context instance. - Coordinator(std::shared_ptr context); + Coordinator(ContextT& context); ~Coordinator(); /// Introspect and include in iteration planning diff --git a/herald/include/herald/payload/simple/simple_payload_data_supplier.h b/herald/include/herald/payload/simple/simple_payload_data_supplier.h index 5e0625d..2ac2221 100644 --- a/herald/include/herald/payload/simple/simple_payload_data_supplier.h +++ b/herald/include/herald/payload/simple/simple_payload_data_supplier.h @@ -11,6 +11,7 @@ #include "../extended/extended_data.h" #include "../../context.h" #include "../../datatype/payload_timestamp.h" +#include "herald/data/sensor_logger.h" #include #include @@ -30,23 +31,88 @@ class SimplePayloadDataSupplier : public PayloadDataSupplier { virtual ~SimplePayloadDataSupplier() = default; }; +template class ConcreteSimplePayloadDataSupplierV1 : public SimplePayloadDataSupplier { public: - ConcreteSimplePayloadDataSupplierV1(std::shared_ptr context, std::uint16_t countryCode, std::uint16_t stateCode, - SecretKey sk, K k); - ConcreteSimplePayloadDataSupplierV1(std::shared_ptr context, std::uint16_t countryCode, std::uint16_t stateCode, - SecretKey sk, K k, ConcreteExtendedDataV1 ext); + ConcreteSimplePayloadDataSupplierV1(ContextT& context, std::uint16_t countryCode, std::uint16_t stateCode, + SecretKey sk, K k) + : SimplePayloadDataSupplier(), + ctx(context), country(countryCode), state(stateCode), secretKey(sk), k(k), + commonPayloadHeader(), extended(), day(-1), contactIdentifiers() + HLOGGERINIT(ctx, "Sensor", "ConcreteSimplePayloadDataSupplierV1") + { + commonPayloadHeader.append(std::uint8_t(0x10)); // Simple payload V1 + commonPayloadHeader.append(country); + commonPayloadHeader.append(state); + + HTDBG("About to call matching keys"); + // matchingKeys = k.matchingKeys(sk); + HTDBG("Completed matching keys call"); + } + ConcreteSimplePayloadDataSupplierV1(ContextT& context, std::uint16_t countryCode, std::uint16_t stateCode, + SecretKey sk, K k, ConcreteExtendedDataV1 ext) + : SimplePayloadDataSupplier(), + ctx(context), country(countryCode), state(stateCode), secretKey(sk), k(k), + commonPayloadHeader(), extended(ext), day(-1), contactIdentifiers() + HLOGGERINIT(ctx, "Sensor", "ConcreteSimplePayloadDataSupplierV1") + { + commonPayloadHeader.append(std::uint8_t(0x10)); // Simple payload V1 + commonPayloadHeader.append(country); + commonPayloadHeader.append(state); + } ConcreteSimplePayloadDataSupplierV1(const ConcreteSimplePayloadDataSupplierV1& from) = delete; // copy ctor deletion ConcreteSimplePayloadDataSupplierV1(ConcreteSimplePayloadDataSupplierV1&& from) = delete; // move ctor deletion - ~ConcreteSimplePayloadDataSupplierV1(); + ~ConcreteSimplePayloadDataSupplierV1() = default; + + std::optional legacyPayload(const PayloadTimestamp timestamp, const std::shared_ptr device) override { + return {}; + } + + std::optional payload(const PayloadTimestamp timestamp, const std::shared_ptr device) override { + + const int day = k.day(timestamp.value); + const int period = k.period(timestamp.value); + + auto cid = k.contactIdentifier(secretKey,day,period); + + PayloadData p(commonPayloadHeader); + // length + if (extended.hasData()) { + p.append(std::uint16_t(2 + extended.payload().value().size())); + } else { + p.append(std::uint16_t(2)); + } + // contact id + p.append(cid); + // extended data + if (extended.hasData()) { + p.append(extended.payload().value()); + } - std::optional legacyPayload(const PayloadTimestamp timestamp, const std::shared_ptr device) override; - std::optional payload(const PayloadTimestamp timestamp, const std::shared_ptr device) override; - std::vector payload(const Data& data) override; + return std::optional{p}; + } + + std::vector payload(const Data& data) override { + return std::vector(); + } private: - class Impl; // fwd decl - std::unique_ptr mImpl; // PIMPL idiom + ContextT& ctx; + const uint16_t country; + const uint16_t state; + const SecretKey secretKey; + K k; + + PayloadData commonPayloadHeader; + // std::vector matchingKeys; + + ConcreteExtendedDataV1 extended; + + // cached day/period info + int day; + std::vector contactIdentifiers; + + HLOGGER(ContextT); }; } diff --git a/herald/include/herald/sensor_array.h b/herald/include/herald/sensor_array.h index 9ef108a..4993ce1 100644 --- a/herald/include/herald/sensor_array.h +++ b/herald/include/herald/sensor_array.h @@ -31,10 +31,11 @@ using namespace payload; /// available on each platform. In Zephyr RTOS, for example, /// This is a simple 250ms delay within a special Herald-only /// Zephyr kernel thread. +template class SensorArray : public Sensor { public: /// \brief Takes ownership of payloadDataSupplier (std::move) - SensorArray(std::shared_ptr ctx, std::shared_ptr payloadDataSupplier); + SensorArray(ContextT& ctx, std::shared_ptr payloadDataSupplier); ~SensorArray(); // SENSOR ARRAY METHODS diff --git a/herald/include/herald/zephyr_context.h b/herald/include/herald/zephyr_context.h index 95aa79f..53132d3 100644 --- a/herald/include/herald/zephyr_context.h +++ b/herald/include/herald/zephyr_context.h @@ -8,6 +8,13 @@ #include "context.h" #include "ble/bluetooth_state_manager.h" +// Herald logging to zephyr - see zephyr_context.cpp for details +#ifndef CONFIG_HERALD_LOG_LEVEL + #define CONFIG_HERALD_LOG_LEVEL 0 +#endif + +#include "data/sensor_logger.h" + #include #include #include @@ -18,11 +25,6 @@ #include #include -// Herald logging to zephyr - see zephyr_context.cpp for details -#ifndef CONFIG_HERALD_LOG_LEVEL - #define CONFIG_HERALD_LOG_LEVEL 0 -#endif - namespace herald { using namespace herald::ble; @@ -45,15 +47,16 @@ namespace zephyrinternal { /// /// \brief Holds generic state across our application for any Zephyr RTOS device. +/// +/// Provides a solid class that holds information and types to be pass to Context /// -class ZephyrContext : public Context, public BluetoothStateManager, public std::enable_shared_from_this { +class ZephyrContextProvider : BluetoothStateManager { public: - ZephyrContext(); - ~ZephyrContext(); + ZephyrContextProvider(); + ~ZephyrContextProvider(); - // Context override methods - std::shared_ptr getLoggingSink(const std::string& subsystemFor, const std::string& categoryFor) override; - std::shared_ptr getBluetoothStateManager() override; + ZephyrLoggingSink& getLoggingSink(); + BluetoothStateManager& getBluetoothStateManager(); // Bluetooth State Manager override methods void add(std::shared_ptr delegate) override; @@ -70,8 +73,13 @@ class ZephyrContext : public Context, public BluetoothStateManager, public std:: void periodicActions() noexcept; private: - class Impl; - std::unique_ptr mImpl; // PIMPL idiom + ZephyrLoggingSink sink; + + zephyrinternal::Advertiser advertiser; + + std::vector> stateDelegates; + + bool bluetoothEnabled; }; diff --git a/herald/src/ble/ble_coordinator.cpp b/herald/src/ble/ble_coordinator.cpp index 828c893..960752c 100644 --- a/herald/src/ble/ble_coordinator.cpp +++ b/herald/src/ble/ble_coordinator.cpp @@ -16,12 +16,13 @@ namespace ble { using namespace herald::engine; -class HeraldProtocolBLECoordinationProvider::Impl { +template +class HeraldProtocolBLECoordinationProvider::Impl { public: - Impl(std::shared_ptr ctx, std::shared_ptr bledb,std::shared_ptr provider); + Impl(ContextT& ctx, std::shared_ptr bledb,std::shared_ptr provider); ~Impl(); - std::shared_ptr context; + ContextT& context; std::shared_ptr db; std::shared_ptr pp; @@ -31,10 +32,11 @@ class HeraldProtocolBLECoordinationProvider::Impl { int breakEvery; int breakFor; - HLOGGER; + HLOGGER(ContextT); }; -HeraldProtocolBLECoordinationProvider::Impl::Impl(std::shared_ptr ctx, std::shared_ptr bledb,std::shared_ptr provider) +template +HeraldProtocolBLECoordinationProvider::Impl::Impl(ContextT& ctx, std::shared_ptr bledb,std::shared_ptr provider) : context(ctx), db(bledb), pp(provider), @@ -47,14 +49,17 @@ HeraldProtocolBLECoordinationProvider::Impl::Impl(std::shared_ptr ctx, ; } -HeraldProtocolBLECoordinationProvider::Impl::~Impl() +template +HeraldProtocolBLECoordinationProvider::Impl::~Impl() { ; } -HeraldProtocolBLECoordinationProvider::HeraldProtocolBLECoordinationProvider(std::shared_ptr ctx, +template +HeraldProtocolBLECoordinationProvider::HeraldProtocolBLECoordinationProvider( + ContextT& ctx, std::shared_ptr db, std::shared_ptr provider) : mImpl(std::make_unique(ctx,db,provider)) { @@ -62,14 +67,16 @@ HeraldProtocolBLECoordinationProvider::HeraldProtocolBLECoordinationProvider(std } -HeraldProtocolBLECoordinationProvider::~HeraldProtocolBLECoordinationProvider() +template +HeraldProtocolBLECoordinationProvider::~HeraldProtocolBLECoordinationProvider() { ; } +template std::vector -HeraldProtocolBLECoordinationProvider::connectionsProvided() +HeraldProtocolBLECoordinationProvider::connectionsProvided() { return std::vector(1,herald::engine::Features::HeraldBluetoothProtocolConnection); } @@ -77,8 +84,9 @@ HeraldProtocolBLECoordinationProvider::connectionsProvided() // void // HeraldProtocolBLECoordinationProvider::provision(const std::vector& requested, // const ConnectionCallback& connCallback) +template std::vector -HeraldProtocolBLECoordinationProvider::provision( +HeraldProtocolBLECoordinationProvider::provision( const std::vector& requested) { if (requested.empty()) { @@ -165,8 +173,9 @@ HeraldProtocolBLECoordinationProvider::provision( +template std::vector>> -HeraldProtocolBLECoordinationProvider::requiredConnections() +HeraldProtocolBLECoordinationProvider::requiredConnections() { std::vector>> results; @@ -302,8 +311,9 @@ HeraldProtocolBLECoordinationProvider::requiredConnections() return results; } +template std::vector -HeraldProtocolBLECoordinationProvider::requiredActivities() +HeraldProtocolBLECoordinationProvider::requiredActivities() { std::vector results; diff --git a/herald/src/ble/concrete_ble_database.cpp b/herald/src/ble/concrete_ble_database.cpp index 01876c3..9fe69c1 100644 --- a/herald/src/ble/concrete_ble_database.cpp +++ b/herald/src/ble/concrete_ble_database.cpp @@ -32,21 +32,23 @@ struct last_updated_descending { -class ConcreteBLEDatabase::Impl { +template +class ConcreteBLEDatabase::Impl { public: - Impl(std::shared_ptr context); + Impl(ContextT& context); ~Impl(); void assignAdvertData(std::shared_ptr& newDevice, std::vector&& toMove, const std::vector& manuData); - std::shared_ptr ctx; + ContextT& ctx; std::vector> delegates; std::vector> devices; - HLOGGER; + HLOGGER(ContextT); }; -ConcreteBLEDatabase::Impl::Impl(std::shared_ptr context) +template +ConcreteBLEDatabase::Impl::Impl(ContextT& context) : ctx(context), delegates(), devices() @@ -55,13 +57,15 @@ ConcreteBLEDatabase::Impl::Impl(std::shared_ptr context) ; } -ConcreteBLEDatabase::Impl::~Impl() +template +ConcreteBLEDatabase::Impl::~Impl() { ; } +template void -ConcreteBLEDatabase::Impl::assignAdvertData(std::shared_ptr& newDevice, std::vector&& toMove, const std::vector& manuData) +ConcreteBLEDatabase::Impl::assignAdvertData(std::shared_ptr& newDevice, std::vector&& toMove, const std::vector& manuData) { newDevice->advertData(std::move(toMove)); @@ -132,27 +136,31 @@ ConcreteBLEDatabase::Impl::assignAdvertData(std::shared_ptr& newDevic -ConcreteBLEDatabase::ConcreteBLEDatabase(std::shared_ptr context) +template +ConcreteBLEDatabase::ConcreteBLEDatabase(ContextT& context) : mImpl(std::make_unique(context)) { ; } -ConcreteBLEDatabase::~ConcreteBLEDatabase() +template +ConcreteBLEDatabase::~ConcreteBLEDatabase() { ; } // BLE Database overrides +template void -ConcreteBLEDatabase::add(const std::shared_ptr& delegate) +ConcreteBLEDatabase::add(const std::shared_ptr& delegate) { mImpl->delegates.push_back(delegate); } +template std::shared_ptr -ConcreteBLEDatabase::device(const PayloadData& payloadData) +ConcreteBLEDatabase::device(const PayloadData& payloadData) { auto results = matches([&payloadData](const std::shared_ptr& d) { auto payload = d->payloadData(); @@ -174,8 +182,9 @@ ConcreteBLEDatabase::device(const PayloadData& payloadData) } +template std::shared_ptr -ConcreteBLEDatabase::device(const BLEMacAddress& mac, const Data& advert/*, const RSSI& rssi*/) +ConcreteBLEDatabase::device(const BLEMacAddress& mac, const Data& advert/*, const RSSI& rssi*/) { // Check by MAC first TargetIdentifier targetIdentifier((Data)mac); @@ -242,8 +251,9 @@ ConcreteBLEDatabase::device(const BLEMacAddress& mac, const Data& advert/*, cons return newDevice; } +template std::shared_ptr -ConcreteBLEDatabase::device(const BLEMacAddress& mac, const BLEMacAddress& pseudo) +ConcreteBLEDatabase::device(const BLEMacAddress& mac, const BLEMacAddress& pseudo) { auto samePseudo = matches([&pseudo](const std::shared_ptr& d) { return d->pseudoDeviceAddress() == pseudo; @@ -276,14 +286,16 @@ ConcreteBLEDatabase::device(const BLEMacAddress& mac, const BLEMacAddress& pseud return newDevice; } +template std::shared_ptr -ConcreteBLEDatabase::device(const BLEMacAddress& mac) +ConcreteBLEDatabase::device(const BLEMacAddress& mac) { return device(TargetIdentifier((Data)mac)); } +template std::shared_ptr -ConcreteBLEDatabase::device(const TargetIdentifier& targetIdentifier) +ConcreteBLEDatabase::device(const TargetIdentifier& targetIdentifier) { auto results = matches([&targetIdentifier](const std::shared_ptr& d) { return d->identifier() == targetIdentifier; @@ -301,14 +313,16 @@ ConcreteBLEDatabase::device(const TargetIdentifier& targetIdentifier) return newDevice; } +template std::size_t -ConcreteBLEDatabase::size() const +ConcreteBLEDatabase::size() const { return mImpl->devices.size(); } +template std::vector> -ConcreteBLEDatabase::matches( +ConcreteBLEDatabase::matches( const std::function&)>& matcher) const { std::vector> results; @@ -322,8 +336,9 @@ ConcreteBLEDatabase::matches( } /// Cannot name a function delete in C++. remove is common. +template void -ConcreteBLEDatabase::remove(const TargetIdentifier& targetIdentifier) +ConcreteBLEDatabase::remove(const TargetIdentifier& targetIdentifier) { auto found = std::find_if(mImpl->devices.begin(),mImpl->devices.end(), [&targetIdentifier](std::shared_ptr& d) -> bool { @@ -340,8 +355,9 @@ ConcreteBLEDatabase::remove(const TargetIdentifier& targetIdentifier) } // BLE Device Delegate overrides +template void -ConcreteBLEDatabase::device(const std::shared_ptr& device, const BLEDeviceAttribute didUpdate) +ConcreteBLEDatabase::device(const std::shared_ptr& device, const BLEDeviceAttribute didUpdate) { // TODO update any internal DB state as necessary (E.g. deletion) for (auto& delegate : mImpl->delegates) { diff --git a/herald/src/ble/concrete_ble_sensor.cpp b/herald/src/ble/concrete_ble_sensor.cpp index f65428a..fe8611c 100644 --- a/herald/src/ble/concrete_ble_sensor.cpp +++ b/herald/src/ble/concrete_ble_sensor.cpp @@ -19,10 +19,11 @@ namespace ble { using namespace herald::datatype; -class ConcreteBLESensor::Impl { +template +class ConcreteBLESensor::Impl { public: - Impl(std::shared_ptr ctx, - std::shared_ptr bluetoothStateManager, + Impl(ContextT& ctx, + BluetoothStateManager& bluetoothStateManager, std::shared_ptr payloadDataSupplier); ~Impl(); @@ -30,22 +31,23 @@ class ConcreteBLESensor::Impl { // Data members hidden by PIMPL - std::shared_ptr database; - std::shared_ptr stateManager; - std::shared_ptr transmitter; - std::shared_ptr receiver; + std::shared_ptr> database; + BluetoothStateManager& stateManager; + std::shared_ptr> transmitter; + std::shared_ptr> receiver; std::vector> delegates; - std::shared_ptr coordinator; + std::shared_ptr> coordinator; bool addedSelfAsDelegate; - HLOGGER; + HLOGGER(ContextT); }; -ConcreteBLESensor::Impl::Impl(std::shared_ptr ctx, - std::shared_ptr bluetoothStateManager, +template +ConcreteBLESensor::Impl::Impl(ContextT& ctx, + BluetoothStateManager& bluetoothStateManager, std::shared_ptr payloadDataSupplier) : database(std::make_shared(ctx)), stateManager(bluetoothStateManager), @@ -56,14 +58,15 @@ ConcreteBLESensor::Impl::Impl(std::shared_ptr ctx, ctx, bluetoothStateManager, payloadDataSupplier, database) ), delegates(), - coordinator(std::make_shared(ctx, database, receiver)), + coordinator(std::make_shared>(ctx, database, receiver)), addedSelfAsDelegate(false) HLOGGERINIT(ctx,"sensor","ConcreteBLESensor") { ; } -ConcreteBLESensor::Impl::~Impl() +template +ConcreteBLESensor::Impl::~Impl() { ; } @@ -73,21 +76,24 @@ ConcreteBLESensor::Impl::~Impl() -ConcreteBLESensor::ConcreteBLESensor(std::shared_ptr ctx, - std::shared_ptr bluetoothStateManager, +template +ConcreteBLESensor::ConcreteBLESensor(ContextT& ctx, + BluetoothStateManager& bluetoothStateManager, std::shared_ptr payloadDataSupplier) : mImpl(std::make_unique(ctx,bluetoothStateManager,payloadDataSupplier)) { ; } -ConcreteBLESensor::~ConcreteBLESensor() +template +ConcreteBLESensor::~ConcreteBLESensor() { ; } +template std::optional> -ConcreteBLESensor::coordinationProvider() +ConcreteBLESensor::coordinationProvider() { // Only return this if we support scanning if (BLESensorConfiguration::scanningEnabled) { @@ -98,21 +104,24 @@ ConcreteBLESensor::coordinationProvider() return {}; } +template bool -ConcreteBLESensor::immediateSend(Data data, const TargetIdentifier& targetIdentifier) +ConcreteBLESensor::immediateSend(Data data, const TargetIdentifier& targetIdentifier) { return mImpl->receiver->immediateSend(data,targetIdentifier); } +template bool -ConcreteBLESensor::immediateSendAll(Data data) +ConcreteBLESensor::immediateSendAll(Data data) { return mImpl->receiver->immediateSendAll(data); } // Sensor overrides +template void -ConcreteBLESensor::add(const std::shared_ptr& delegate) +ConcreteBLESensor::add(const std::shared_ptr& delegate) { mImpl->delegates.push_back(delegate); // add all delegates to receiver and transmitter too? @@ -121,11 +130,12 @@ ConcreteBLESensor::add(const std::shared_ptr& delegate) // TODO what about duplicates? } +template void -ConcreteBLESensor::start() +ConcreteBLESensor::start() { if (!mImpl->addedSelfAsDelegate) { - mImpl->stateManager->add(shared_from_this()); // FAILS IF USED IN THE CTOR - DO NOT DO THIS FROM CTOR + mImpl->stateManager.add(shared_from_this()); // FAILS IF USED IN THE CTOR - DO NOT DO THIS FROM CTOR mImpl->database->add(shared_from_this()); mImpl->addedSelfAsDelegate = true; } @@ -136,8 +146,9 @@ ConcreteBLESensor::start() } } +template void -ConcreteBLESensor::stop() +ConcreteBLESensor::stop() { mImpl->transmitter->stop(); mImpl->receiver->stop(); @@ -146,17 +157,19 @@ ConcreteBLESensor::stop() } } +template // Database overrides void -ConcreteBLESensor::bleDatabaseDidCreate(const std::shared_ptr& device) +ConcreteBLESensor::bleDatabaseDidCreate(const std::shared_ptr& device) { for (auto& delegate : mImpl->delegates) { delegate->sensor(SensorType::BLE, device->identifier()); // didDetect } } +template void -ConcreteBLESensor::bleDatabaseDidUpdate(const std::shared_ptr& device, +ConcreteBLESensor::bleDatabaseDidUpdate(const std::shared_ptr& device, const BLEDeviceAttribute attribute) { switch (attribute) { @@ -216,15 +229,17 @@ ConcreteBLESensor::bleDatabaseDidUpdate(const std::shared_ptr& device } } +template void -ConcreteBLESensor::bleDatabaseDidDelete(const std::shared_ptr& device) +ConcreteBLESensor::bleDatabaseDidDelete(const std::shared_ptr& device) { ; // TODO just log this // TODO determine if to pass this on too } // Bluetooth state manager delegate overrides +template void -ConcreteBLESensor::bluetoothStateManager(BluetoothState didUpdateState) +ConcreteBLESensor::bluetoothStateManager(BluetoothState didUpdateState) { if (BluetoothState::poweredOff == didUpdateState) { // stop(); diff --git a/herald/src/ble/zephyr/concrete_ble_transmitter.cpp b/herald/src/ble/zephyr/concrete_ble_transmitter.cpp index 74f479d..7175895 100644 --- a/herald/src/ble/zephyr/concrete_ble_transmitter.cpp +++ b/herald/src/ble/zephyr/concrete_ble_transmitter.cpp @@ -75,14 +75,14 @@ namespace zephyrinternal { class ConcreteBLETransmitter::Impl { public: - Impl(std::shared_ptr ctx, std::shared_ptr bluetoothStateManager, + Impl(Context& ctx, std::shared_ptr bluetoothStateManager, std::shared_ptr payloadDataSupplier, std::shared_ptr bleDatabase); ~Impl(); void startAdvertising(); void stopAdvertising(); - std::shared_ptr m_context; + Context& m_context; std::shared_ptr m_stateManager; std::shared_ptr m_pds; std::shared_ptr m_db; @@ -218,9 +218,9 @@ static struct bt_data ad[] = { ConcreteBLETransmitter::Impl::Impl( - std::shared_ptr ctx, std::shared_ptr bluetoothStateManager, + Context& ctx, std::shared_ptr bluetoothStateManager, std::shared_ptr payloadDataSupplier, std::shared_ptr bleDatabase) - : m_context(std::static_pointer_cast(ctx)), // Herald API guarantees this to be safe + : m_context(ctx), m_stateManager(bluetoothStateManager), m_pds(payloadDataSupplier), m_db(bleDatabase), @@ -309,7 +309,7 @@ ConcreteBLETransmitter::Impl::stopAdvertising() ConcreteBLETransmitter::ConcreteBLETransmitter( - std::shared_ptr ctx, std::shared_ptr bluetoothStateManager, + Context& ctx, std::shared_ptr bluetoothStateManager, std::shared_ptr payloadDataSupplier, std::shared_ptr bleDatabase) : mImpl(std::make_shared(ctx,bluetoothStateManager, payloadDataSupplier,bleDatabase)) diff --git a/herald/src/context.cpp b/herald/src/context.cpp deleted file mode 100644 index d01f58d..0000000 --- a/herald/src/context.cpp +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright 2020-2021 Herald Project Contributors -// SPDX-License-Identifier: Apache-2.0 -// - -#include "herald/context.h" -#include "herald/data/sensor_logger.h" - -#include - -namespace herald { - -// TODO filter this default logger by CONFIG_HERALD_LOG_LEVEL - -class StdOutLoggingSink : public SensorLoggingSink { -public: - StdOutLoggingSink(const std::string& subsystemFor, const std::string& categoryFor) - : m_subsystem(subsystemFor), m_category(categoryFor) - { - ; - } - ~StdOutLoggingSink() - { - ; - } - - void log(SensorLoggerLevel level, std::string message) override - { - std::string lvl = "info"; - switch (level) { - case SensorLoggerLevel::debug: - lvl = "debug"; - break; - case SensorLoggerLevel::fault: - lvl = "fault"; - break; - default: - break; - } - std::cout << m_subsystem << "," << m_category << "," - << lvl << "," << message << std::endl; - } - -private: - const std::string m_subsystem; - const std::string m_category; -}; - -std::shared_ptr -DefaultContext::getLoggingSink(const std::string& subsystemFor, const std::string& categoryFor) -{ - return std::make_shared(subsystemFor,categoryFor); -} - -std::shared_ptr -DefaultContext::getBluetoothStateManager() -{ - return nullptr; -} - - -} \ No newline at end of file diff --git a/herald/src/data/contact_log.cpp b/herald/src/data/contact_log.cpp deleted file mode 100644 index 443b2a0..0000000 --- a/herald/src/data/contact_log.cpp +++ /dev/null @@ -1,170 +0,0 @@ -// Copyright 2021 Herald Project Contributors -// SPDX-License-Identifier: Apache-2.0 -// - -#include "herald/data/contact_log.h" -#include "herald/data/sensor_logger.h" -#include "herald/datatype/payload_data.h" -#include "herald/datatype/target_identifier.h" - -#include - -namespace herald { -namespace data { - -using namespace herald::datatype; - -class ErrorStreamContactLogger::Impl { -public: - Impl(std::shared_ptr context, std::shared_ptr formatter); - ~Impl(); - - std::string csv(std::string toEscape) const noexcept; - std::string timestamp() const noexcept; - - std::shared_ptr ctx; - std::shared_ptr fmt; - - HLOGGER; -}; - -ErrorStreamContactLogger::Impl::Impl(std::shared_ptr context, std::shared_ptr formatter) - : ctx(context), - fmt(formatter) - HLOGGERINIT(ctx,"Sensor","contacts.log") -{ - // logger.fault("ctor test"); - ; -} - -ErrorStreamContactLogger::Impl::~Impl() = default; - -std::string -ErrorStreamContactLogger::Impl::csv(std::string toEscape) const noexcept -{ - // C++23 only: if (toEscape.contains(",") || toEscape.contains("\"") || toEscape.contains("'") || toEscape.contains("’")) { - // Pre C++23:- - if (std::string::npos != toEscape.find(",") || - std::string::npos != toEscape.find("\"") || - std::string::npos != toEscape.find("'") || - std::string::npos != toEscape.find("’")) { - return "\"" + toEscape + "\""; - } - return toEscape; -} - -std::string -ErrorStreamContactLogger::Impl::timestamp() const noexcept -{ - return Date().iso8601DateTime(); -} - - - - - -ErrorStreamContactLogger::ErrorStreamContactLogger(std::shared_ptr context, std::shared_ptr formatter) - : mImpl(std::make_unique(context, formatter)) -{ - ; -} - - -ErrorStreamContactLogger::~ErrorStreamContactLogger() = default; - -// Sensor delegate overrides -void -ErrorStreamContactLogger::sensor(SensorType sensor, const TargetIdentifier& didDetect) -{ - HDBG("didDetect"); - // HDBG("flibble"); - // std::string s = mImpl->timestamp(); - // s += ","; - // s += str(sensor); - // s += ","; - // s += mImpl->csv((std::string)didDetect); - // s += ",1,,,,,"; - // HERR(s); -} - -void -ErrorStreamContactLogger::sensor(SensorType sensor, const PayloadData& didRead, const TargetIdentifier& fromTarget) -{ - // std::string s = mImpl->timestamp(); - // s += ","; - // s += str(sensor); - // s += ","; - // s += mImpl->csv((std::string)fromTarget); - // s += ",,2,,,,"; - // s += mImpl->csv(mImpl->fmt->shortFormat(didRead)); - - // HERR(s); -} - -void -ErrorStreamContactLogger::sensor(SensorType sensor, const ImmediateSendData& didReceive, const TargetIdentifier& fromTarget) -{ - ; -} - -void -ErrorStreamContactLogger::sensor(SensorType sensor, const std::vector& didShare, const TargetIdentifier& fromTarget) -{ - // std::string s = mImpl->timestamp(); - // s += ","; - // s += str(sensor); - // s += ","; - // s += mImpl->csv((std::string)fromTarget); - // s += ",,,,4,,"; - // // std::string prefix = mImpl->timestamp() + "," + str(sensor) + "," + mImpl->csv((std::string)fromTarget) + ",,,,4,,"; - // for (auto& payload : didShare) { - // std::string final = s; // copy - // final += mImpl->csv(mImpl->fmt->shortFormat(payload)); - // HERR(final); - // // HERR(prefix + mImpl->csv(mImpl->fmt->shortFormat(payload))); - // } -} - -void -ErrorStreamContactLogger::sensor(SensorType sensor, const Proximity& didMeasure, const TargetIdentifier& fromTarget) -{ - // std::string s = mImpl->timestamp(); - // s += ","; - // s += str(sensor); - // s += ","; - // s += mImpl->csv((std::string)fromTarget); - // s += ",,,3,,,"; - // s += mImpl->csv((std::string)didMeasure); - // HERR(s); - - // HERR(mImpl->timestamp() + "," + str(sensor) + "," + mImpl->csv((std::string)fromTarget) + ",,,3,,," + mImpl->csv((std::string)didMeasure)); -} - -template -void -ErrorStreamContactLogger::sensor(SensorType sensor, const Location& didVisit) -{ - // std::string s = mImpl->timestamp(); - // s += ","; - // s += str(sensor); - // s += ",,,,,,5,"; - // s += mImpl->csv((std::string)didVisit); - // HERR(s); - - // HERR(mImpl->timestamp() + "," + str(sensor) + ",,,,,,5," + mImpl->csv((std::string)didVisit)); -} - -void -ErrorStreamContactLogger::sensor(SensorType sensor, const Proximity& didMeasure, const TargetIdentifier& fromTarget, const PayloadData& withPayload) -{ - ; -} - -void -ErrorStreamContactLogger::sensor(SensorType sensor, const SensorState& didUpdateState) -{ - ; -} - -} -} \ No newline at end of file diff --git a/herald/src/data/stdout_logging_sink.cpp b/herald/src/data/stdout_logging_sink.cpp new file mode 100644 index 0000000..eb2a0ea --- /dev/null +++ b/herald/src/data/stdout_logging_sink.cpp @@ -0,0 +1,33 @@ +// Copyright 2020-2021 Herald Project Contributors +// SPDX-License-Identifier: Apache-2.0 +// + +#include "herald/data/sensor_logger.h" +#include "herald/data/stdout_logging_sink.h" + +#include + +namespace herald::data { + +StdOutLoggingSink::StdOutLoggingSink() = default; +StdOutLoggingSink::~StdOutLoggingSink() = default; + +void +StdOutLoggingSink::log(const std::string& subsystem, const std::string& category, SensorLoggerLevel level, std::string message) +{ + std::string lvl = "info"; + switch (level) { + case SensorLoggerLevel::debug: + lvl = "debug"; + break; + case SensorLoggerLevel::fault: + lvl = "fault"; + break; + default: + break; + } + std::cout << subsystem << "," << category << "," + << lvl << "," << message << std::endl; +} + +} \ No newline at end of file diff --git a/herald/src/data/zephyr/zephyr_logging_sink.cpp b/herald/src/data/zephyr/zephyr_logging_sink.cpp new file mode 100644 index 0000000..ee6508c --- /dev/null +++ b/herald/src/data/zephyr/zephyr_logging_sink.cpp @@ -0,0 +1,35 @@ +// Copyright 2021 Herald Project Contributors +// SPDX-License-Identifier: Apache-2.0 +// + +#include "herald/data/sensor_logger.h" + +#include + +namespace herald { +namespace data { + +ZephyrLoggingSink::ZephyrLoggingSink() = default; +ZephyrLoggingSink::~ZephyrLoggingSink() = default; + +void +ZephyrLoggingSink::log(const std::string& subsystem,const std::string& category, + SensorLoggerLevel level, const std::string& message) +{ + // TODO be more specific? Filter here or in Zephyr? + std::string finalMessage = subsystem + "," + category + "," + message; + switch (level) { + case SensorLoggerLevel::debug: + LOG_DBG("%s",log_strdup(finalMessage.c_str())); + break; + case SensorLoggerLevel::fault: + LOG_ERR("%s",log_strdup(finalMessage.c_str())); + break; + default: + LOG_INF("%s",log_strdup(finalMessage.c_str())); + break; + } +} + +} +} \ No newline at end of file diff --git a/herald/src/engine/coordinator.cpp b/herald/src/engine/coordinator.cpp index 397bae2..0b5b146 100644 --- a/herald/src/engine/coordinator.cpp +++ b/herald/src/engine/coordinator.cpp @@ -21,22 +21,24 @@ namespace engine { using namespace herald::datatype; -class Coordinator::Impl { +template +class Coordinator::Impl { public: - Impl(std::shared_ptr ctx); + Impl(ContextT& ctx); ~Impl(); - std::shared_ptr context; + ContextT& context; std::vector> providers; std::map> featureProviders; bool running; - HLOGGER; + HLOGGER(ContextT); }; -Coordinator::Impl::Impl(std::shared_ptr ctx) +template +Coordinator::Impl::Impl(ContextT& ctx) : context(ctx), providers(), running(false) @@ -45,7 +47,8 @@ Coordinator::Impl::Impl(std::shared_ptr ctx) ; } -Coordinator::Impl::~Impl() +template +Coordinator::Impl::~Impl() { ; } @@ -53,21 +56,24 @@ Coordinator::Impl::~Impl() -Coordinator::Coordinator(std::shared_ptr ctx) +template +Coordinator::Coordinator(ContextT& ctx) : mImpl(std::make_unique(ctx)) { ; } -Coordinator::~Coordinator() +template +Coordinator::~Coordinator() { ; } /** Introspect and include in iteration planning **/ +template void -Coordinator::add(std::shared_ptr sensor) +Coordinator::add(std::shared_ptr sensor) { HDBG("Adding sensor"); auto prov = sensor->coordinationProvider(); @@ -78,15 +84,17 @@ Coordinator::add(std::shared_ptr sensor) } /** Remove from iteration planning **/ +template void -Coordinator::remove(std::shared_ptr sensor) +Coordinator::remove(std::shared_ptr sensor) { // TODO support remove } /** Prepares for iterations to be called (may pre-emptively make calls) **/ +template void -Coordinator::start() +Coordinator::start() { HDBG("Start called"); // Clear feature providers @@ -103,8 +111,9 @@ Coordinator::start() } /** Execute an iteration of activity, according to settings **/ +template void -Coordinator::iteration() +Coordinator::iteration() { if (!mImpl->running) { HDBG("Coordinator not running. Returning from iteration having done nothing."); @@ -220,8 +229,9 @@ Coordinator::iteration() } /** Closes out any existing connections/activities **/ +template void -Coordinator::stop() +Coordinator::stop() { mImpl->running = false; // No-op - done in other methods (for now) diff --git a/herald/src/payload/simple/simple_payload_data_supplier.cpp b/herald/src/payload/simple/simple_payload_data_supplier.cpp index a288d2c..ffc1c88 100644 --- a/herald/src/payload/simple/simple_payload_data_supplier.cpp +++ b/herald/src/payload/simple/simple_payload_data_supplier.cpp @@ -18,182 +18,175 @@ namespace herald { namespace payload { namespace simple { -using namespace herald::payload::extended; - -class ConcreteSimplePayloadDataSupplierV1::Impl { -public: - Impl(std::shared_ptr context, std::uint16_t countryCode, std::uint16_t stateCode, SecretKey sk, K k); - Impl(std::shared_ptr context, std::uint16_t countryCode, std::uint16_t stateCode, SecretKey sk, K k, ConcreteExtendedDataV1 ext); - ~Impl(); - - std::shared_ptr ctx; - const uint16_t country; - const uint16_t state; - const SecretKey secretKey; - K k; - - PayloadData commonPayloadHeader; - // std::vector matchingKeys; - - ConcreteExtendedDataV1 extended; - - // cached day/period info - int day; - std::vector contactIdentifiers; - - HLOGGER -}; - - -ConcreteSimplePayloadDataSupplierV1::Impl::Impl(std::shared_ptr context, std::uint16_t countryCode, std::uint16_t stateCode, - SecretKey sk, K k) - : ctx(context), country(countryCode), state(stateCode), secretKey(sk), k(k), commonPayloadHeader(), extended(), day(-1), contactIdentifiers() - HLOGGERINIT(ctx, "Sensor", "ConcreteSimplePayloadDataSupplierV1") -{ - commonPayloadHeader.append(std::uint8_t(0x10)); // Simple payload V1 - commonPayloadHeader.append(country); - commonPayloadHeader.append(state); - - HTDBG("About to call matching keys"); - // matchingKeys = k.matchingKeys(sk); - HTDBG("Completed matching keys call"); - - // // add length (no extended data) - // payload.append(std::uint16_t(2)); - - // // TODO generate data from secret key - // payload.append(std::uint64_t(0)); - // payload.append(std::uint64_t(0)); -} - - -ConcreteSimplePayloadDataSupplierV1::Impl::Impl(std::shared_ptr context, std::uint16_t countryCode, std::uint16_t stateCode, - SecretKey sk, K k, ConcreteExtendedDataV1 ext) - : ctx(context), country(countryCode), state(stateCode), secretKey(sk), k(k), commonPayloadHeader(), extended(ext), day(-1), contactIdentifiers() - HLOGGERINIT(ctx, "Sensor", "ConcreteSimplePayloadDataSupplierV1") -{ - commonPayloadHeader.append(std::uint8_t(0x10)); // Simple payload V1 - commonPayloadHeader.append(country); - commonPayloadHeader.append(state); - - // matchingKeys = k.matchingKeys(sk); - - // // add length (with extended data) - // if (extended.hasData()) { - // payload.append(std::uint16_t(2 + extended.payload().value().size())); - // } else { - // payload.append(std::uint16_t(2)); - // } - - // // TODO generate data from secret key - // payload.append(std::uint64_t(0)); - // payload.append(std::uint64_t(0)); - - // auto extPayload = extended.payload(); - // if (extPayload.has_value()) { - // payload.append(extPayload.value()); - // } -} - -ConcreteSimplePayloadDataSupplierV1::Impl::~Impl() -{ - ; -} - - - - - - -ConcreteSimplePayloadDataSupplierV1::ConcreteSimplePayloadDataSupplierV1(std::shared_ptr context, std::uint16_t countryCode, std::uint16_t stateCode, - SecretKey sk, K k) - : SimplePayloadDataSupplier(), - mImpl(std::make_unique(context, countryCode,stateCode,sk,k)) -{ - ; -} - -ConcreteSimplePayloadDataSupplierV1::ConcreteSimplePayloadDataSupplierV1(std::shared_ptr context, std::uint16_t countryCode, std::uint16_t stateCode, - SecretKey sk, K k, ConcreteExtendedDataV1 ext) - : SimplePayloadDataSupplier(), - mImpl(std::make_unique(context, countryCode,stateCode,sk,k,ext)) -{ - ; -} - -ConcreteSimplePayloadDataSupplierV1::~ConcreteSimplePayloadDataSupplierV1() -{ - ; -} - -std::optional -ConcreteSimplePayloadDataSupplierV1::legacyPayload(const PayloadTimestamp timestamp, const std::shared_ptr device) -{ - return {}; -} - -std::optional -ConcreteSimplePayloadDataSupplierV1::payload(const PayloadTimestamp timestamp, const std::shared_ptr device) -{ - const int day = mImpl->k.day(timestamp.value); - const int period = mImpl->k.period(timestamp.value); - - // if (!(day >= 0 && day < mImpl->matchingKeys.size())) { - // HERR("Contact identifier out of day range"); - // return {}; - // } - - auto cid = mImpl->k.contactIdentifier(mImpl->secretKey,day,period); - - // if (-1 == mImpl->day || day != mImpl->day) { - // // generate new matching keys - // mImpl->day = day; - // auto contactKeys = mImpl->k.contactKeys(mImpl->matchingKeys[day]); - // mImpl->contactIdentifiers.clear(); - // mImpl->contactIdentifiers.reserve(contactKeys.size()); - // for (int i = 0;i < contactKeys.size();i++) { - // mImpl->contactIdentifiers.emplace_back(); - // } - // for (int i = contactKeys.size() - 1;i >= 0;i--) { - // mImpl->contactIdentifiers[i].append(mImpl->k.contactIdentifier(contactKeys[i])); - // } - // } - - // contact identifiers is always populated, so no error condition check here - - // if (!(period >=0 && period < mImpl->contactIdentifiers.size())) { - // HERR("Contact identifier out of period range"); - // return {}; - // } - - // Defensive check - // if (mImpl->contactIdentifiers[period].size() != 16) { - // HERR("Contact identifier not 16 bytes"); - // return {}; - // } - - PayloadData p(mImpl->commonPayloadHeader); - // length - if (mImpl->extended.hasData()) { - p.append(std::uint16_t(2 + mImpl->extended.payload().value().size())); - } else { - p.append(std::uint16_t(2)); - } - // contact id - p.append(cid); - // extended data - if (mImpl->extended.hasData()) { - p.append(mImpl->extended.payload().value()); - } - - return std::optional{p}; -} - -std::vector -ConcreteSimplePayloadDataSupplierV1::payload(const Data& data) -{ - return std::vector(); -} +// using namespace herald::payload::extended; + +// class ConcreteSimplePayloadDataSupplierV1::Impl { +// public: +// Impl(Context& context, std::uint16_t countryCode, std::uint16_t stateCode, SecretKey sk, K k); +// Impl(Context& context, std::uint16_t countryCode, std::uint16_t stateCode, SecretKey sk, K k, ConcreteExtendedDataV1 ext); +// ~Impl(); +// }; + + +// ConcreteSimplePayloadDataSupplierV1::Impl::Impl(Context& context, std::uint16_t countryCode, std::uint16_t stateCode, +// SecretKey sk, K k) +// : ctx(context), country(countryCode), state(stateCode), secretKey(sk), k(k), commonPayloadHeader(), extended(), day(-1), contactIdentifiers() +// HLOGGERINIT(ctx, "Sensor", "ConcreteSimplePayloadDataSupplierV1") +// { + +// // // add length (no extended data) +// // payload.append(std::uint16_t(2)); + +// // // TODO generate data from secret key +// // payload.append(std::uint64_t(0)); +// // payload.append(std::uint64_t(0)); +// } + + +// ConcreteSimplePayloadDataSupplierV1::Impl::Impl(Context& context, std::uint16_t countryCode, std::uint16_t stateCode, +// SecretKey sk, K k, ConcreteExtendedDataV1 ext) +// : ctx(context), country(countryCode), state(stateCode), secretKey(sk), k(k), commonPayloadHeader(), extended(ext), day(-1), contactIdentifiers() +// HLOGGERINIT(ctx, "Sensor", "ConcreteSimplePayloadDataSupplierV1") +// { + +// // matchingKeys = k.matchingKeys(sk); + +// // // add length (with extended data) +// // if (extended.hasData()) { +// // payload.append(std::uint16_t(2 + extended.payload().value().size())); +// // } else { +// // payload.append(std::uint16_t(2)); +// // } + +// // // TODO generate data from secret key +// // payload.append(std::uint64_t(0)); +// // payload.append(std::uint64_t(0)); + +// // auto extPayload = extended.payload(); +// // if (extPayload.has_value()) { +// // payload.append(extPayload.value()); +// // } +// } + +// ConcreteSimplePayloadDataSupplierV1::Impl::~Impl() +// { +// ; +// } + + + + + + +// template +// ConcreteSimplePayloadDataSupplierV1::ConcreteSimplePayloadDataSupplierV1( +// ContextT& context, std::uint16_t countryCode, std::uint16_t stateCode, +// SecretKey sk, K k) +// : SimplePayloadDataSupplier(), +// ctx(context), country(countryCode), state(stateCode), secretKey(sk), k(k), +// commonPayloadHeader(), extended(), day(-1), contactIdentifiers() +// HLOGGERINIT(ctx, "Sensor", "ConcreteSimplePayloadDataSupplierV1") +// { +// commonPayloadHeader.append(std::uint8_t(0x10)); // Simple payload V1 +// commonPayloadHeader.append(country); +// commonPayloadHeader.append(state); + +// HTDBG("About to call matching keys"); +// // matchingKeys = k.matchingKeys(sk); +// HTDBG("Completed matching keys call"); +// } + +// template +// ConcreteSimplePayloadDataSupplierV1::ConcreteSimplePayloadDataSupplierV1( +// ContextT& context, std::uint16_t countryCode, std::uint16_t stateCode, +// SecretKey sk, K k, ConcreteExtendedDataV1 ext) +// : SimplePayloadDataSupplier(), +// ctx(context), country(countryCode), state(stateCode), secretKey(sk), k(k), +// commonPayloadHeader(), extended(ext), day(-1), contactIdentifiers() +// HLOGGERINIT(ctx, "Sensor", "ConcreteSimplePayloadDataSupplierV1") +// { +// commonPayloadHeader.append(std::uint8_t(0x10)); // Simple payload V1 +// commonPayloadHeader.append(country); +// commonPayloadHeader.append(state); +// } + +// template +// ConcreteSimplePayloadDataSupplierV1::~ConcreteSimplePayloadDataSupplierV1() +// { +// ; +// } + +// template +// std::optional +// ConcreteSimplePayloadDataSupplierV1::legacyPayload(const PayloadTimestamp timestamp, const std::shared_ptr device) +// { +// return {}; +// } + +// template +// std::optional +// ConcreteSimplePayloadDataSupplierV1::payload(const PayloadTimestamp timestamp, const std::shared_ptr device) +// { +// const int day = mImpl->k.day(timestamp.value); +// const int period = mImpl->k.period(timestamp.value); + +// // if (!(day >= 0 && day < mImpl->matchingKeys.size())) { +// // HERR("Contact identifier out of day range"); +// // return {}; +// // } + +// auto cid = mImpl->k.contactIdentifier(mImpl->secretKey,day,period); + +// // if (-1 == mImpl->day || day != mImpl->day) { +// // // generate new matching keys +// // mImpl->day = day; +// // auto contactKeys = mImpl->k.contactKeys(mImpl->matchingKeys[day]); +// // mImpl->contactIdentifiers.clear(); +// // mImpl->contactIdentifiers.reserve(contactKeys.size()); +// // for (int i = 0;i < contactKeys.size();i++) { +// // mImpl->contactIdentifiers.emplace_back(); +// // } +// // for (int i = contactKeys.size() - 1;i >= 0;i--) { +// // mImpl->contactIdentifiers[i].append(mImpl->k.contactIdentifier(contactKeys[i])); +// // } +// // } + +// // contact identifiers is always populated, so no error condition check here + +// // if (!(period >=0 && period < mImpl->contactIdentifiers.size())) { +// // HERR("Contact identifier out of period range"); +// // return {}; +// // } + +// // Defensive check +// // if (mImpl->contactIdentifiers[period].size() != 16) { +// // HERR("Contact identifier not 16 bytes"); +// // return {}; +// // } + +// PayloadData p(mImpl->commonPayloadHeader); +// // length +// if (mImpl->extended.hasData()) { +// p.append(std::uint16_t(2 + mImpl->extended.payload().value().size())); +// } else { +// p.append(std::uint16_t(2)); +// } +// // contact id +// p.append(cid); +// // extended data +// if (mImpl->extended.hasData()) { +// p.append(mImpl->extended.payload().value()); +// } + +// return std::optional{p}; +// } + +// template +// std::vector +// ConcreteSimplePayloadDataSupplierV1::payload(const Data& data) +// { +// return std::vector(); +// } } } diff --git a/herald/src/sensor_array.cpp b/herald/src/sensor_array.cpp index 85ae084..370c616 100644 --- a/herald/src/sensor_array.cpp +++ b/herald/src/sensor_array.cpp @@ -23,31 +23,33 @@ using namespace datatype; using namespace payload; using namespace engine; -class SensorArray::Impl { +template +class SensorArray::Impl { public: - Impl(std::shared_ptr ctx, std::shared_ptr payloadDataSupplier); + Impl(ContextT& ctx, std::shared_ptr payloadDataSupplier); ~Impl(); // Initialised on entry to Impl constructor:- - std::shared_ptr mContext; + ContextT& mContext; std::shared_ptr mPayloadDataSupplier; std::vector> mSensorArray; - std::shared_ptr concrete; + std::shared_ptr> concrete; - Coordinator engine; + Coordinator engine; // Not initialised (and thus optional):- std::string deviceDescription; - HLOGGER; + HLOGGER(ContextT); }; -SensorArray::Impl::Impl(std::shared_ptr ctx, std::shared_ptr payloadDataSupplier) +template +SensorArray::Impl::Impl(ContextT& ctx, std::shared_ptr payloadDataSupplier) : mContext(ctx), mPayloadDataSupplier(payloadDataSupplier), mSensorArray(), - concrete(std::make_shared(mContext, mContext->getBluetoothStateManager(), + concrete(std::make_shared>(mContext, mContext.getBluetoothStateManager(), mPayloadDataSupplier)), engine(ctx), deviceDescription("") @@ -71,7 +73,8 @@ SensorArray::Impl::Impl(std::shared_ptr ctx, std::shared_ptr +SensorArray::Impl::~Impl() { ; } @@ -82,66 +85,76 @@ SensorArray::Impl::~Impl() /// Takes ownership of payloadDataSupplier (std::move) -SensorArray::SensorArray(std::shared_ptr ctx, std::shared_ptr payloadDataSupplier) +template +SensorArray::SensorArray(ContextT& ctx, std::shared_ptr payloadDataSupplier) : mImpl(std::make_unique(ctx,payloadDataSupplier)) { ; } -SensorArray::~SensorArray() +template +SensorArray::~SensorArray() { ; } // SENSOR ARRAY METHODS +template bool -SensorArray::immediateSend(Data data, const TargetIdentifier& targetIdentifier) { +SensorArray::immediateSend(Data data, const TargetIdentifier& targetIdentifier) { return mImpl->concrete->immediateSend(data, targetIdentifier); } +template bool -SensorArray::immediateSendAll(Data data) { +SensorArray::immediateSendAll(Data data) { return mImpl->concrete->immediateSendAll(data); } +template std::optional -SensorArray::payloadData() { +SensorArray::payloadData() { return mImpl->mPayloadDataSupplier->payload(PayloadTimestamp(),nullptr); } // SENSOR OVERRIDES +template void -SensorArray::add(const std::shared_ptr& delegate) { +SensorArray::add(const std::shared_ptr& delegate) { for (auto& sensor: mImpl->mSensorArray) { sensor->add(delegate); } } +template void -SensorArray::start() { +SensorArray::start() { for (auto& sensor: mImpl->mSensorArray) { sensor->start(); } mImpl->engine.start(); } +template void -SensorArray::stop() { +SensorArray::stop() { mImpl->engine.stop(); for (auto& sensor: mImpl->mSensorArray) { sensor->stop(); } } +template std::optional> -SensorArray::coordinationProvider() +SensorArray::coordinationProvider() { return {}; } // Periodic actions +template void -SensorArray::iteration(const TimeInterval sinceLastCompleted) +SensorArray::iteration(const TimeInterval sinceLastCompleted) { // TODO ensure this works for continuous evaluation with minimal overhead or battery mImpl->engine.iteration(); diff --git a/herald/src/zephyr_context.cpp b/herald/src/zephyr_context.cpp index 0f142bb..fe6d4d4 100644 --- a/herald/src/zephyr_context.cpp +++ b/herald/src/zephyr_context.cpp @@ -5,12 +5,12 @@ // #include "herald/datatype/stdlib.h" // hashing of std::pair #include "herald/zephyr_context.h" +#include "herald/data/zephyr/zephyr_logging_sink.h" #include "herald/data/sensor_logger.h" #include "herald/ble/bluetooth_state_manager.h" #include "herald/ble/bluetooth_state_manager_delegate.h" #include "herald/datatype/bluetooth_state.h" - #include #include #include @@ -20,65 +20,12 @@ #include #include -// NOTE: Link Herald to the Zephyr logging system -// Set HERALD_LOG_LEVEL=4 for debug in CMake using add_definitions(-DHERALD_LOG_LEVEL=4 ) -// Defaults to 0 (OFF) - see zephyr_context.h -#include - namespace herald { -// THE BELOW IS DONE IN EXACTLY ONE HERALD FILE -LOG_MODULE_REGISTER(heraldlogger, HERALD_LOG_LEVEL); - using namespace herald::data; using namespace herald::datatype; using namespace herald::ble; -// HIDDEN LOGGING SINK IMPLEMENTATION FOR ZEPHYR - -class ZephyrLoggingSink : public SensorLoggingSink { -public: - ZephyrLoggingSink(const std::string& subsystem, const std::string& category); - ~ZephyrLoggingSink(); - - void log(SensorLoggerLevel level, std::string message) override; - -private: - std::string m_subsystem; - std::string m_category; -}; - - - -ZephyrLoggingSink::ZephyrLoggingSink(const std::string& subsystem, const std::string& category) - : m_subsystem(subsystem), m_category(category) -{ - ; -} - -ZephyrLoggingSink::~ZephyrLoggingSink() { - ; -} - -void -ZephyrLoggingSink::log(SensorLoggerLevel level, std::string message) -{ - // TODO be more specific? Filter here or in Zephyr? - std::string finalMessage = m_subsystem + "," + m_category + "," + message; - switch (level) { - case SensorLoggerLevel::debug: - LOG_DBG("%s",log_strdup(finalMessage.c_str())); - break; - case SensorLoggerLevel::fault: - LOG_ERR("%s",log_strdup(finalMessage.c_str())); - break; - default: - LOG_INF("%s",log_strdup(finalMessage.c_str())); - break; - } -} - - // ADVERTISER SPECIFICATION namespace zephyrinternal { @@ -135,90 +82,44 @@ Advertiser::registerStartCallback(std::function cb) } // end namespace -// HIDDEN CONTEXT IMPLEMENTATION FOR ZEPHYR - - -class ZephyrContext::Impl { -public: - Impl(); - ~Impl(); - - // Any Zephyr RTOS specific global handles go here - bool enabled; - - std::vector> stateDelegates; - - // pair is subsystem and category - std::map, - std::shared_ptr> logSinks; - - zephyrinternal::Advertiser advertiser; -}; - -ZephyrContext::Impl::Impl() - : enabled(false), - stateDelegates(), - logSinks(), - advertiser() -{ - ; -} - -ZephyrContext::Impl::~Impl() -{ - ; -} - - - - // ZEPHYR CONTEXT PUBLIC INTERFACE METHODS // Zephyr RTOS implementation of Context -ZephyrContext::ZephyrContext() - : mImpl(std::make_unique()) +ZephyrContextProvider::ZephyrContextProvider() + : sink(), + advertiser(), + stateDelegates(), + bluetoothEnabled(false) { ; } -ZephyrContext::~ZephyrContext() -{ - ; -} +ZephyrContextProvider::~Context() = default; -std::shared_ptr -ZephyrContext::getLoggingSink(const std::string& subsystemFor, const std::string& categoryFor) +ZephyrLoggingSink& +ZephyrContextProvider::getLoggingSink() { - std::pair id(subsystemFor,categoryFor); - auto foundSinkIndex = mImpl->logSinks.find(id); - if (mImpl->logSinks.end() != foundSinkIndex) { - return foundSinkIndex->second; - } - std::shared_ptr newSink = std::make_shared( - subsystemFor, categoryFor - ); - mImpl->logSinks.emplace(id, newSink); - return newSink; + return sink; } -std::shared_ptr -ZephyrContext::getBluetoothStateManager() +BluetoothStateManager& +ZephyrContextProvider::getBluetoothStateManager() { - return shared_from_this(); + return *this } void -ZephyrContext::add(std::shared_ptr delegate) +ZephyrContextProvider::add(std::shared_ptr delegate) { - mImpl->stateDelegates.push_back(delegate); + stateDelegates.push_back(delegate); } BluetoothState -ZephyrContext::state() +ZephyrContextProvider::state() { // TODO support detection of Bluetooth being unsupported, and power cycling/resetting states - if (mImpl->enabled) { + if (bluetoothEnabled) { return BluetoothState::poweredOn; } else { return BluetoothState::poweredOff; @@ -226,15 +127,15 @@ ZephyrContext::state() } zephyrinternal::Advertiser& -ZephyrContext::getAdvertiser() noexcept +ZephyrContextProvider::getAdvertiser() noexcept { - return mImpl->advertiser; + return advertiser; } int -ZephyrContext::enableBluetooth() noexcept +ZephyrContextProvider::enableBluetooth() noexcept { - LOG_INF("ZephyrContext::enableBluetooth"); + LOG_INF("Context::enableBluetooth"); int success; // TODO determine if default Zephyr mac address rotation uses Nordic CC3xx, if present @@ -272,9 +173,9 @@ ZephyrContext::enableBluetooth() noexcept LOG_INF("settings load returned"); } if (0 == success) { - mImpl->enabled = true; + bluetoothEnabled = true; - for (auto delegate : mImpl->stateDelegates) { + for (auto delegate : stateDelegates) { delegate->bluetoothStateManager(BluetoothState::poweredOn); } } else { @@ -285,25 +186,25 @@ ZephyrContext::enableBluetooth() noexcept } int -ZephyrContext::startBluetooth() noexcept +ZephyrContextProvider::startBluetooth() noexcept { - if (!mImpl->enabled) { + if (!bluetoothEnabled) { return enableBluetooth(); } return 0; // success } int -ZephyrContext::stopBluetooth() noexcept +ZephyrContextProvider::stopBluetooth() noexcept { - for (auto delegate : mImpl->stateDelegates) { + for (auto delegate : stateDelegates) { delegate->bluetoothStateManager(BluetoothState::poweredOff); } return 0; } void -ZephyrContext::periodicActions() noexcept +ZephyrContextProvider::periodicActions() noexcept { // TODO periodic bluetooth actions // E.g. determine if we should rotate mac address (if not done for us?) From 2171b54be9c71bbcc59bf514bc5201b2ffde73a8 Mon Sep 17 00:00:00 2001 From: Adam Fowler Date: Sat, 27 Mar 2021 16:48:13 +0000 Subject: [PATCH 18/28] Core library and tests passing with Template mechanism for Context Known issues:- - Requires testing in app / zephyr Signed-off-by: Adam Fowler --- herald-tests/sensorlogger-tests.cpp | 16 +- herald-tests/simplepayload-tests.cpp | 4 +- herald-tests/test-templates.h | 4 +- herald/include/herald/ble/ble_concrete.h | 283 +++++- herald/include/herald/ble/ble_coordinator.h | 374 +++++++- herald/include/herald/ble/ble_database.h | 2 +- herald/include/herald/data/sensor_logger.h | 83 +- herald/include/herald/engine/coordinator.h | 179 +++- herald/src/ble/ble_coordinator.cpp | 807 +++++++++--------- herald/src/ble/concrete_ble_database.cpp | 346 -------- herald/src/engine/coordinator.cpp | 228 ----- .../simple/simple_payload_data_supplier.cpp | 169 ---- 12 files changed, 1259 insertions(+), 1236 deletions(-) diff --git a/herald-tests/sensorlogger-tests.cpp b/herald-tests/sensorlogger-tests.cpp index fcf3e4e..b485212 100644 --- a/herald-tests/sensorlogger-tests.cpp +++ b/herald-tests/sensorlogger-tests.cpp @@ -15,7 +15,7 @@ TEST_CASE("sensorlogger-output-dbg", "[sensorlogger][output]") { DummyLoggingSink dls; DummyBluetoothStateManager dbsm; herald::Context ctx(dls,dbsm); // default context include - using CT = typename herald::Context; + // using CT = typename herald::Context; herald::data::SensorLogger logger(ctx.getLoggingSink(),"testout","mytest"); HTDBG("Simple string"); @@ -54,7 +54,7 @@ TEST_CASE("sensorlogger-output-log", "[sensorlogger][output]") { DummyLoggingSink dls; DummyBluetoothStateManager dbsm; herald::Context ctx(dls,dbsm); // default context include - using CT = typename herald::Context; + // using CT = typename herald::Context; herald::data::SensorLogger logger(ctx.getLoggingSink(),"testout","mytest"); HTLOG("Simple string"); @@ -88,7 +88,7 @@ TEST_CASE("sensorlogger-output-fault", "[sensorlogger][output]") { DummyLoggingSink dls; DummyBluetoothStateManager dbsm; herald::Context ctx(dls,dbsm); // default context include - using CT = typename herald::Context; + // using CT = typename herald::Context; herald::data::SensorLogger logger(ctx.getLoggingSink(),"testout","mytest"); HTERR("Simple string"); @@ -123,7 +123,7 @@ TEST_CASE("sensorlogger-output-intrinsic", "[sensorlogger][output]") { DummyLoggingSink dls; DummyBluetoothStateManager dbsm; herald::Context ctx(dls,dbsm); // default context include - using CT = typename herald::Context; + // using CT = typename herald::Context; herald::data::SensorLogger logger(ctx.getLoggingSink(),"testout","mytest"); int i = 37; @@ -184,7 +184,7 @@ TEST_CASE("sensorlogger-bug-negativesuccess", "[sensorlogger][output][bug]") { DummyLoggingSink dls; DummyBluetoothStateManager dbsm; herald::Context ctx(dls,dbsm); // default context include - using CT = typename herald::Context; + // using CT = typename herald::Context; herald::data::SensorLogger logger(ctx.getLoggingSink(),"testout","mytest"); int success = -22; @@ -200,7 +200,7 @@ TEST_CASE("sensorlogger-bug-targetidatend", "[sensorlogger][output][bug]") { DummyLoggingSink dls; DummyBluetoothStateManager dbsm; herald::Context ctx(dls,dbsm); // default context include - using CT = typename herald::Context; + // using CT = typename herald::Context; herald::data::SensorLogger logger(ctx.getLoggingSink(),"testout","mytest"); herald::datatype::TargetIdentifier t(herald::datatype::Data(std::byte(0x09),3)); @@ -217,7 +217,7 @@ TEST_CASE("sensorlogger-output-data", "[sensorlogger][output]") { DummyLoggingSink dls; DummyBluetoothStateManager dbsm; herald::Context ctx(dls,dbsm); // default context include - using CT = typename herald::Context; + // using CT = typename herald::Context; herald::data::SensorLogger logger(ctx.getLoggingSink(),"testout","mytest"); herald::datatype::Data t(std::byte(0x09),3); @@ -234,7 +234,7 @@ TEST_CASE("sensorlogger-output-targetidentifier", "[sensorlogger][output]") { DummyLoggingSink dls; DummyBluetoothStateManager dbsm; herald::Context ctx(dls,dbsm); // default context include - using CT = typename herald::Context; + // using CT = typename herald::Context; herald::data::SensorLogger logger(ctx.getLoggingSink(),"testout","mytest"); herald::datatype::TargetIdentifier t(herald::datatype::Data(std::byte(0x09),3)); diff --git a/herald-tests/simplepayload-tests.cpp b/herald-tests/simplepayload-tests.cpp index 5fb45f0..6e065c0 100644 --- a/herald-tests/simplepayload-tests.cpp +++ b/herald-tests/simplepayload-tests.cpp @@ -240,7 +240,7 @@ TEST_CASE("payload-simple-basic", "[payload][simple][basic]") { DummyLoggingSink dls; DummyBluetoothStateManager dbsm; herald::Context ctx(dls,dbsm); // default context include - using CT = typename herald::Context; + // using CT = typename herald::Context; std::uint16_t country = 826; std::uint16_t state = 4; herald::payload::simple::K k; @@ -279,7 +279,7 @@ TEST_CASE("payload-simple-payloadbounds", "[payload][simple][payloadbounds]") { DummyLoggingSink dls; DummyBluetoothStateManager dbsm; herald::Context ctx(dls,dbsm); // default context include - using CT = typename herald::Context; + // using CT = typename herald::Context; std::uint16_t country = 826; std::uint16_t state = 4; herald::payload::simple::K k; diff --git a/herald-tests/test-templates.h b/herald-tests/test-templates.h index 7a93070..d733f55 100644 --- a/herald-tests/test-templates.h +++ b/herald-tests/test-templates.h @@ -7,13 +7,15 @@ #include "herald/herald.h" +#include + struct DummyLoggingSink { DummyLoggingSink() : subsystem(), category(), value() {} ~DummyLoggingSink() = default; void log(const std::string& sub,const std::string& cat,herald::data::SensorLoggerLevel level, std::string message) { - // std::cout << "DummyLogger::log" << std::endl; value = sub + "," + cat + "," + message; + std::cout << "DummyLoggingSink::log: " << value << std::endl; subsystem = sub; category = cat; } diff --git a/herald/include/herald/ble/ble_concrete.h b/herald/include/herald/ble/ble_concrete.h index 9ac9606..a60f6a9 100644 --- a/herald/include/herald/ble/ble_concrete.h +++ b/herald/include/herald/ble/ble_concrete.h @@ -12,15 +12,20 @@ #include "ble_protocols.h" #include "bluetooth_state_manager.h" #include "ble_device_delegate.h" +#include "filter/ble_advert_parser.h" #include "../payload/payload_data_supplier.h" #include "../context.h" +#include "../data/sensor_logger.h" #include +#include +#include namespace herald { namespace ble { using namespace herald::datatype; +using namespace herald::ble::filter; using namespace herald::payload; // NOTE THIS HEADER IS FOR ALL PLATFORMS. @@ -39,44 +44,294 @@ class ConcreteBluetoothStateManager : public BluetoothStateManager, public std:: BluetoothState state() override; }; +/// \brief Provides a callable that assists in ordering for most recently updated BLEDevice +struct last_updated_descending { + bool operator()(const std::shared_ptr& a, const std::shared_ptr& b) { + return a->timeIntervalSinceLastUpdate() > b->timeIntervalSinceLastUpdate(); // opposite order + } +}; + template class ConcreteBLEDatabase : public BLEDatabase, public BLEDeviceDelegate, public std::enable_shared_from_this> { public: - ConcreteBLEDatabase(ContextT& context); + ConcreteBLEDatabase(ContextT& context) + : ctx(context), + delegates(), + devices() + HLOGGERINIT(context,"herald","ConcreteBLEDatabase") + { + ; + } + ConcreteBLEDatabase(const ConcreteBLEDatabase& from) = delete; ConcreteBLEDatabase(ConcreteBLEDatabase&& from) = delete; - ~ConcreteBLEDatabase(); + + ~ConcreteBLEDatabase() = default; // BLE Database overrides - void add(const std::shared_ptr& delegate) override; + void add(const std::shared_ptr& delegate) override { + delegates.push_back(delegate); + } // Creation overrides - std::shared_ptr device(const BLEMacAddress& mac, const Data& advert/*, const RSSI& rssi*/) override; - std::shared_ptr device(const BLEMacAddress& mac, const BLEMacAddress& pseudo) override; - std::shared_ptr device(const BLEMacAddress& mac) override; - std::shared_ptr device(const PayloadData& payloadData) override; - std::shared_ptr device(const TargetIdentifier& targetIdentifier) override; + std::shared_ptr device(const BLEMacAddress& mac, const Data& advert/*, const RSSI& rssi*/) override { + // Check by MAC first + TargetIdentifier targetIdentifier((Data)mac); + auto results = matches([&targetIdentifier](const std::shared_ptr& d) { + return d->identifier() == targetIdentifier; + }); + if (results.size() != 0) { + // HDBG("DEVICE ALREADY KNOWN BY MAC"); + // Assume advert details are known already + return results.front(); // TODO ensure we send back the latest, not just the first match + // res->rssi(rssi); + // return res; + } + + // Now check by pseudo mac + auto segments = BLEAdvertParser::extractSegments(advert,0); + // HTDBG("segments:-"); + // HTDBG(std::to_string(segments.size())); + auto manuData = BLEAdvertParser::extractManufacturerData(segments); + auto heraldDataSegments = BLEAdvertParser::extractHeraldManufacturerData(manuData); + // HTDBG("herald data segments:-"); + // HTDBG(std::to_string(heraldDataSegments.size())); + // auto device = db->device(bleMacAddress); // For most devices this will suffice + + // TODO Check for public herald service in ADV_IND packet - shown if an Android device, wearable or beacon in zephyr + // auto serviceData128 = BLEAdvertParser::extractServiceUUID128Data(segments); + // bool hasHeraldService = false; + // for (auto& service : serviceData128) { + // if (service.uuid == heraldUuidData) { + // hasHeraldService = true; + // HTDBG("FOUND DEVICE ADVERTISING HERALD SERVICE"); + // device->operatingSystem(BLEDeviceOperatingSystem::android); + // } + // } + + + if (0 != heraldDataSegments.size()) { + // HDBG("Found Herald Android pseudo device address in advert"); + // Try to FIND by pseudo first + BLEMacAddress pseudo(heraldDataSegments.front()); + auto samePseudo = matches([&pseudo](const std::shared_ptr& d) { + return d->pseudoDeviceAddress() == pseudo; + }); + if (0 != samePseudo.size()) { + // HDBG("FOUND EXISTING DEVICE BY PSEUDO"); + return samePseudo.front(); + } + // HDBG("CREATING NEW DEVICE BY MAC AND PSEUDO ONLY"); + // Now create new device with mac and pseudo + auto newDevice = device(mac,pseudo); + assignAdvertData(newDevice,std::move(segments), manuData); + // newDevice->rssi(rssi); + return newDevice; + } + + // HDBG("CREATING NEW DEVICE BY MAC ONLY"); + + // Now create a device just from a mac + auto newDevice = device(targetIdentifier); + // HDBG("Got new device"); + assignAdvertData(newDevice,std::move(segments), manuData); + // newDevice->rssi(rssi); + // HDBG("Assigned advert data"); + return newDevice; + } + + std::shared_ptr device(const BLEMacAddress& mac, const BLEMacAddress& pseudo) override { + auto samePseudo = matches([&pseudo](const std::shared_ptr& d) { + return d->pseudoDeviceAddress() == pseudo; + }); + if (0 == samePseudo.size()) { + auto ptr = device(TargetIdentifier((Data)pseudo)); + ptr->pseudoDeviceAddress(pseudo); + return ptr; + } + // get most recent and clone, then attach + auto comp = last_updated_descending(); + std::sort(samePseudo.begin(),samePseudo.end(), comp); // functional style + auto newDevice = std::make_shared(*samePseudo.front()); // copy ctor used + // TODO support calling card + // auto toShare = shareDataAcrossDevices(pseudo); + // if (toShare.has_value()) { + // newDevice.payloadData(toShare); + // } + + // Has pseudo address so must be android + newDevice->operatingSystem(BLEDeviceOperatingSystem::android); + + // register new device discovery date + newDevice->registerDiscovery(Date()); + + devices.push_back(newDevice); + for (auto delegate : delegates) { + delegate->bleDatabaseDidCreate(newDevice); + } + return newDevice; + } + + std::shared_ptr device(const BLEMacAddress& mac) override { + return device(TargetIdentifier((Data)mac)); + } + + std::shared_ptr device(const PayloadData& payloadData) override { + auto results = matches([&payloadData](const std::shared_ptr& d) { + auto payload = d->payloadData(); + if (!payload.has_value()) { + return false; + } + return (*payload)==payloadData; + }); + if (results.size() != 0) { + return results.front(); // TODO ensure we send back the latest, not just the first match + } + std::shared_ptr newDevice = std::make_shared( + TargetIdentifier(payloadData), this->shared_from_this()); + devices.push_back(newDevice); + for (auto delegate : delegates) { + delegate->bleDatabaseDidCreate(newDevice); + } + return newDevice; + } + std::shared_ptr device(const TargetIdentifier& targetIdentifier) override { + auto results = matches([&targetIdentifier](const std::shared_ptr& d) { + return d->identifier() == targetIdentifier; + }); + if (results.size() != 0) { + return results.front(); // TODO ensure we send back the latest, not just the first match + } + HTDBG("New target identified: {}",(std::string)targetIdentifier); + std::shared_ptr newDevice = std::make_shared( + targetIdentifier, this->shared_from_this()); + devices.push_back(newDevice); + for (auto delegate : delegates) { + delegate->bleDatabaseDidCreate(newDevice); + } + return newDevice; + } // Introspection overrides - std::size_t size() const override; + std::size_t size() const override { + return devices.size(); + } std::vector> matches( - const std::function&)>& matcher) const override; + const std::function&)>& matcher) const override { + std::vector> results; + // in the absence of copy_if in C++20... Just copies the pointers not the objects + for (auto& d : devices) { + if (matcher(d)) { + results.push_back(d); + } + } + return results; + } // std::vector> devices() const override; /// Cannot name a function delete in C++. remove is common. - void remove(const TargetIdentifier& targetIdentifier) override; + void remove(const TargetIdentifier& targetIdentifier) override { + auto found = std::find_if(devices.begin(),devices.end(), + [&targetIdentifier](std::shared_ptr& d) -> bool { + return d->identifier() == targetIdentifier; + } + ); + if (found != devices.end()) { + std::shared_ptr toRemove = *found; + devices.erase(found); + for (auto& delegate : delegates) { + delegate->bleDatabaseDidDelete(toRemove); + } + } + } // std::optional payloadSharingData(const std::shared_ptr& peer) override; // BLE Device Delegate overrides - void device(const std::shared_ptr& device, BLEDeviceAttribute didUpdate) override; + void device(const std::shared_ptr& device, BLEDeviceAttribute didUpdate) override { + // TODO update any internal DB state as necessary (E.g. deletion) + for (auto& delegate : delegates) { + delegate->bleDatabaseDidUpdate(device, didUpdate); // TODO verify this is the right onward call + } + } private: - class Impl; - std::unique_ptr mImpl; // unique as this is handled internally for all platforms by Herald + void assignAdvertData(std::shared_ptr& newDevice, std::vector&& toMove, + const std::vector& manuData) + { + newDevice->advertData(std::move(toMove)); + + // If it's an apple device, check to see if its on our ignore list + auto appleDataSegments = BLEAdvertParser::extractAppleManufacturerSegments(manuData); + if (0 != appleDataSegments.size()) { + HTDBG("Found apple device"); + // HTDBG((std::string)mac); + newDevice->operatingSystem(BLEDeviceOperatingSystem::ios); + // TODO see if we should ignore this Apple device + // TODO abstract these out eventually in to BLEDevice class + bool ignore = false; + /* + "^10....04", + "^10....14", + "^0100000000000000000000000000000000", + "^05","^07","^09", + "^00","^1002","^06","^08","^03","^0C","^0D","^0F","^0E","^0B" + */ + for (auto& segment : appleDataSegments) { + HTDBG(segment.data.hexEncodedString()); + switch (segment.type) { + case 0x00: + case 0x05: + case 0x07: + case 0x09: + case 0x06: + case 0x08: + case 0x03: + case 0x0C: + case 0x0D: + case 0x0F: + case 0x0E: + case 0x0B: + ignore = true; + break; + case 0x10: + // check if second is 02 + if (segment.data.at(0) == std::byte(0x02)) { + ignore = true; + } else { + // Check 3rd data bit for 14 or 04 + if (segment.data.at(2) == std::byte(0x04) || segment.data.at(2) == std::byte(0x14)) { + ignore = true; + } + } + break; + default: + break; + } + } + if (ignore) { + HTDBG(" - Ignoring Apple device due to Apple data filter"); + newDevice->ignore(true); + } else { + // Perform GATT service discovery to check for Herald service + // NOTE: Happens from Connection request (handled by BLE Coordinator) + HTDBG(" - Unknown apple device... Logging so we can discover services later"); + } + } else { + // Not a Herald android or any iOS - so inspect later (beacon or wearable) + HTDBG("Unknown non Herald device - inspecting (might be a venue beacon or wearable)"); + // HTDBG((std::string)mac); + } + } + + ContextT& ctx; + std::vector> delegates; + std::vector> devices; + + HLOGGER(ContextT); }; /** diff --git a/herald/include/herald/ble/ble_coordinator.h b/herald/include/herald/ble/ble_coordinator.h index 9682250..4218c51 100644 --- a/herald/include/herald/ble/ble_coordinator.h +++ b/herald/include/herald/ble/ble_coordinator.h @@ -9,6 +9,11 @@ #include "../sensor.h" #include "ble_database.h" #include "ble_protocols.h" +#include "ble_coordinator.h" +#include "../engine/activities.h" +#include "ble_protocols.h" +#include "../data/sensor_logger.h" +#include "ble_sensor_configuration.h" #include #include @@ -21,26 +26,379 @@ namespace ble { template class HeraldProtocolBLECoordinationProvider : public CoordinationProvider { public: - HeraldProtocolBLECoordinationProvider(ContextT& ctx, std::shared_ptr db, std::shared_ptr provider); - ~HeraldProtocolBLECoordinationProvider(); + HeraldProtocolBLECoordinationProvider(ContextT& ctx, std::shared_ptr bledb, + std::shared_ptr provider) + : context(ctx), + db(bledb), + pp(provider), + previouslyProvisioned(), + iterationsSinceBreak(0), + breakEvery(10), + breakFor(10) + HLOGGERINIT(ctx,"heraldble","coordinationprovider") + {} + + ~HeraldProtocolBLECoordinationProvider() = default; // Overrides /** What connections does this Sensor type provide for Coordination **/ - std::vector connectionsProvided() override; + std::vector connectionsProvided() override { + return std::vector(1,herald::engine::Features::HeraldBluetoothProtocolConnection); + } + // void provision(const std::vector& requested, // const ConnectionCallback& connCallback) override; std::vector provision( - const std::vector& requested) override; + const std::vector& requested) override { + if (requested.empty()) { + // HTDBG("No connections requested for provisioning"); + } else { + HTDBG("Provisioning connections"); + } + + // Remove those previously provisoned that we no longer require + for (auto& previous : previouslyProvisioned) { + bool found = false; + for (auto& req : requested) { + found = found || std::get<2>(req)==std::get<2>(previous); // comparing target values + } + if (!found) { + HTDBG(" - Found connection to remove"); + auto& optTarget = std::get<2>(previous); + // We can't disconnect from 'all', so check for target + if (optTarget.has_value()) { + // Ignoring closed true/false result + pp->closeConnection(optTarget.value()); + } + } + } + // Now provision new connections + std::vector provisioned; + // For this provider, a prerequisite is a connection to a remote target identifier over Bluetooth + auto requestIter = requested.cbegin(); + bool lastConnectionSuccessful = true; + while (/*currentConnections < maxConnections && */ + lastConnectionSuccessful && + requestIter != requested.cend()) { + HTDBG(" - Satisfying prereq"); + // HTDBG(" - currentConnections currently:-"); + // HTDBG(std::to_string(currentConnections)); + auto& req = *requestIter; + // See if we're already connected + // If so, add to provisioned list + // If not, try to connect + auto& optTarget = std::get<2>(req); + if (optTarget.has_value()) { + HTDBG(" - Have defined target for this prerequisite. Requesting connection be made available."); + // std::future fut = std::async(std::launch::async, + // &HeraldProtocolV1Provider::openConnection,pp, + // optTarget.value(),[&lastConnectionSuccessful] ( + // const TargetIdentifier& targetForConnection, bool success) -> void { + // lastConnectionSuccessful = success; + // }); + // fut.get(); // TODO FIND OUT HOW TO DO THIS FUTURE WAITING FUNCTIONALITY + + lastConnectionSuccessful = pp->openConnection(optTarget.value()); + + // If successful, add to provisioned list + if (lastConnectionSuccessful) { + HTDBG(" - Ensuring connection successful"); + provisioned.push_back(req); + // currentConnections++; + } else { + HTDBG(" - Ensuring connection UNSUCCESSFUL"); + } + } else { + HTDBG(" - No defined target, returning satisfied - always true for Herald BLE"); + // if target not specified then it just means any open connection, so return OK + provisioned.push_back(req); + // TODO determine what to do here if sensor stopped, bluetooth disabled, or no connections open + } + // move forward in iterator + requestIter++; + + lastConnectionSuccessful = true; + } + + previouslyProvisioned = provisioned; + + // TODO schedule disconnection from not required items (E.g. after minimum connection time) + // - we already do this if provision is called, but not after a time period + // - Bluetooth timeout will deal with the underlying connection, but not any intermediate state in the PP instance + + // HTDBG("Returning from provision"); + // connCallback(provisioned); + return provisioned; + } // Runtime coordination callbacks /** Get a list of what connections are required to which devices now (may start, maintain, end (if not included)) **/ - std::vector requiredConnections() override; - std::vector requiredActivities() override; + std::vector requiredConnections() override { + std::vector>> results; + + // This ensures we break from making connections to allow advertising and scanning + iterationsSinceBreak++; + if (iterationsSinceBreak >= breakEvery && + iterationsSinceBreak < (breakEvery + breakFor) ) { + HTDBG("###### Skipping connections - giving advertising & scanning a chance"); + // if (iterationsSinceBreak == breakEvery) { // incase it fails + pp->restartScanningAndAdvertising(); + // } + return results; + } else if (iterationsSinceBreak == (breakEvery + breakFor) ) { + // reset + iterationsSinceBreak = 0; + } + + // Remove expired devices + auto expired = db->matches([/*this*/] (const std::shared_ptr& device) -> bool { + auto interval = device->timeIntervalSinceLastUpdate(); + bool notZero = interval != TimeInterval::zero(); + bool isOld = interval > TimeInterval::minutes(15); + // HTDBG("ID, created, Now, interval, notZero, isOld:-"); + // HTDBG((std::string)device->identifier()); + // HTDBG(std::to_string((long)device->created())); + // HTDBG(std::to_string((long)Date())); + // HTDBG((std::string)interval); + // HTDBG(notZero?"true":"false"); + // HTDBG(isOld?"true":"false"); + return notZero && isOld; + }); + for (auto& exp : expired) { + db->remove(exp->identifier()); + HTDBG("Removing expired device with ID: "); + HTDBG((std::string)exp->identifier()); + HTDBG("time since last update:-"); + HTDBG(std::to_string(exp->timeIntervalSinceLastUpdate())); + } + + // Allow updates from ignored (for a time) status, to retry status + auto tempIgnoredOS = db->matches([](const std::shared_ptr& device) -> bool { + return device->operatingSystem() == BLEDeviceOperatingSystem::ignore; + }); + for (auto& device : tempIgnoredOS) { + // don't bother with separate activity right now - no connection required + device->operatingSystem(BLEDeviceOperatingSystem::unknown); + } + + + // Add all targets in database that are not known + auto newConns = db->matches([](const std::shared_ptr& device) -> bool { + return !device->ignore() && + ( + !device->hasService(BLESensorConfiguration::serviceUUID) + || + !device->payloadData().has_value() // Know the OS, but not the payload (ID) + || + device->immediateSendData().has_value() + ) + ; + }); + for (auto& device : newConns) { + results.emplace_back(herald::engine::Features::HeraldBluetoothProtocolConnection, + herald::engine::Priorities::High, + device->identifier() + ); + } + + // TODO any other devices we may have outstanding work for that requires connections + + // DEBUG ONLY ELEMENTS + if (newConns.size() > 0) { + // print debug info about the BLE Database + HTDBG("BLE DATABASE CURRENT CONTENTS:-"); + auto allDevices = db->matches([](const std::shared_ptr& device) -> bool { + return true; + }); + for (auto& device : allDevices) { + std::string di(" - "); + BLEMacAddress mac((Data)device->identifier()); + di += (std::string)mac; + di += ", created="; + di += std::to_string(device->created()); + di += ", pseudoAddress="; + auto pseudo = device->pseudoDeviceAddress(); + if (pseudo.has_value()) { + di += (std::string)pseudo.value(); + } else { + di += "unset"; + } + di += ", os="; + auto os = device->operatingSystem(); + if (os.has_value()) { + if (herald::ble::BLEDeviceOperatingSystem::ios == os) { + di += "ios"; + } else if (herald::ble::BLEDeviceOperatingSystem::android == os) { + di += "android"; + } else if (herald::ble::BLEDeviceOperatingSystem::unknown == os) { + di += "unknown"; + } else if (herald::ble::BLEDeviceOperatingSystem::ignore == os) { + di += "ignore"; + } else if (herald::ble::BLEDeviceOperatingSystem::android_tbc == os) { + di += "android_tbc"; + } else if (herald::ble::BLEDeviceOperatingSystem::ios_tbc == os) { + di += "ios_tbc"; + } else if (herald::ble::BLEDeviceOperatingSystem::shared == os) { + di += "shared"; + } + } else { + di += "unknown/unset"; + } + di += ", ignore="; + auto ignore = device->ignore(); + if (ignore) { + di += "true (for "; + di += std::to_string(device->timeIntervalUntilIgnoreExpired().millis()); + di += " more secs)"; + } else { + di += "false"; + } + di += ", hasServices="; + di += (device->hasServicesSet() ? "true" : "false"); + di += ", hasReadPayload="; + di += (device->payloadData().has_value() ? device->payloadData().value().hexEncodedString() : "false"); + HTDBG(di); + } + } else { + // restart scanning when no connection activity is expected + pp->restartScanningAndAdvertising(); + } + + return results; + } + std::vector requiredActivities() override { + std::vector results; + + // State 0 - New device -> Read full advert data to see if DCT/Herald -> State Z, 1 or State 3 + // State 1 - Discover services for DCT/Herald on this device -> State Z, or 2 + // State 2 - New Herald BLE device -> Read payload -> State 3 + // State 3 - Steady state - do nothing + // State 4 - Has immediateSend data -> Send immediate -> State 3 + // TODO check for nearby payloads + // State 3 - Not seen in a while -> State X + // State X - Out of range. No actions. + // State Z - Ignore (not a relevant device for Herald... right now). Ignore for a period of time. No actions. + + // TODO is IOS and needs payload sharing + + + // auto state0Devices = db->matches([](std::shared_ptr device) -> bool { + // return !device->ignore() && !device->pseudoDeviceAddress().has_value(); + // }); + auto state1Devices = db->matches([](const std::shared_ptr& device) -> bool { + return !device->ignore() && + !device->receiveOnly() && + !device->hasService(BLESensorConfiguration::serviceUUID); + }); + auto state2Devices = db->matches([](const std::shared_ptr& device) -> bool { + return !device->ignore() && + !device->receiveOnly() && + device->hasService(BLESensorConfiguration::serviceUUID) && + !device->payloadData().has_value(); // TODO check for Herald transferred payload data (not legacy) + }); + auto state4Devices = db->matches([](const std::shared_ptr& device) -> bool { + return !device->ignore() && + !device->receiveOnly() && + device->hasService(BLESensorConfiguration::serviceUUID) && + device->immediateSendData().has_value(); + }); + // TODO State X (timed out / out of range) devices filter check -> Then remove from BLEDatabase + + // NOTE State 0 is handled by the Herald BLE scan function, and so has no specific activity + + // State 1 - discovery Herald service + for (auto& device : state1Devices) { + results.emplace_back(Activity{ + .priority = Priorities::High + 10, + .name = "herald-service-discovery", + .prerequisites = std::vector>>{ + 1, + std::tuple>{ + herald::engine::Features::HeraldBluetoothProtocolConnection, + device->identifier() + } + }, + // .executor = [this](const Activity activity, CompletionCallback callback) -> void { + // // fill this out + // pp->serviceDiscovery(activity,callback); + // } + .executor = [this](const Activity activity) -> std::optional { + // fill this out + pp->serviceDiscovery(activity); + return {}; + } + }); + } + + // State 2 - read herald payload(s) + for (auto& device : state2Devices) { + results.emplace_back(Activity{ + .priority = Priorities::High + 9, + .name = "herald-read-payload", + .prerequisites = std::vector>>{ + 1, + std::tuple>{ + herald::engine::Features::HeraldBluetoothProtocolConnection, + device->identifier() + } + }, + // .executor = [this](const Activity activity, CompletionCallback callback) -> void { + // // fill this out + // pp->readPayload(activity,callback); + // } + .executor = [this](const Activity activity) -> std::optional { + // fill this out + pp->readPayload(activity); + return {}; + } + }); + } + // TODO add check for sensor config payload timeout in above IF + // TODO add BLESensorConfiguration.deviceIntrospectionEnabled && device.supportsModelCharacteristic() && device.model() == null + // TODO add BLESensorConfiguration.deviceIntrospectionEnabled && device.supportsDeviceNameCharacteristic() && device.deviceName() == null + + // State 4 - Has data for immediate send + for (auto& device : state4Devices) { + results.emplace_back(Activity{ + .priority = Priorities::Default + 10, + .name = "herald-immediate-send-targeted", + .prerequisites = std::vector>>{ + 1, + std::tuple>{ + herald::engine::Features::HeraldBluetoothProtocolConnection, + device->identifier() + } + }, + // For std::async based platforms:- + // .executor = [this](const Activity activity, CompletionCallback callback) -> void { + // // fill this out + // pp->immediateSend(activity,callback); + // } + .executor = [this](const Activity activity) -> std::optional { + // fill this out + pp->immediateSend(activity); + return {}; + } + }); + // TODO add immediate send all support + // TODO add read of nearby payload data from remotes + } + return results; +} private: - class Impl; - std::unique_ptr mImpl; + ContextT& context; + std::shared_ptr db; + std::shared_ptr pp; + + std::vector previouslyProvisioned; + + int iterationsSinceBreak; + int breakEvery; + int breakFor; + + HLOGGER(ContextT); }; } diff --git a/herald/include/herald/ble/ble_database.h b/herald/include/herald/ble/ble_database.h index 5a5b74d..98bb713 100644 --- a/herald/include/herald/ble/ble_database.h +++ b/herald/include/herald/ble/ble_database.h @@ -44,7 +44,7 @@ class BLEDatabase { virtual std::size_t size() const = 0; virtual std::vector> matches( - const std::function&)>& matcher) const = 0; + const std::function&)>& matcher) const = 0; /// Cannot name a function delete in C++. remove is common. virtual void remove(const TargetIdentifier& targetIdentifier) = 0; diff --git a/herald/include/herald/data/sensor_logger.h b/herald/include/herald/data/sensor_logger.h index 5e0cade..3e3962e 100644 --- a/herald/include/herald/data/sensor_logger.h +++ b/herald/include/herald/data/sensor_logger.h @@ -135,47 +135,47 @@ namespace { return; } os << c; - pos++; + ++pos; } } - // template - // void tprintf(std::stringstream& os, const std::string& format, std::uint8_t value, Targs... Fargs) // recursive variadic function - // { - // std::size_t pos = 0; - // for ( auto c : format ) { - // if ( c == '{' ) { - // os << std::uint16_t(value); - // if (format.size() > pos + 1 && format.at(pos + 1) == '}') { - // tprintf(os, format.substr(pos + 2), Fargs...); // recursive call - // } else { - // tprintf(os, format.substr(pos + 1), Fargs...); // recursive call - // } - // return; - // } - // os << c; - // pos++; - // } - // } + template + void tprintf(std::stringstream& os, const std::string& format, std::uint8_t value, Targs... Fargs) // recursive variadic function + { + std::size_t pos = 0; + for ( auto c : format ) { + if ( c == '{' ) { + os << std::uint16_t(value); + if (format.size() > pos + 1 && format.at(pos + 1) == '}') { + tprintf(os, format.substr(pos + 2), Fargs...); // recursive call + } else { + tprintf(os, format.substr(pos + 1), Fargs...); // recursive call + } + return; + } + os << c; + ++pos; + } + } - // template - // void tprintf(std::stringstream& os, const std::string& format, std::int8_t value, Targs... Fargs) // recursive variadic function - // { - // std::size_t pos = 0; - // for ( auto c : format ) { - // if ( c == '{' ) { - // os << std::int16_t(value); - // if (format.size() > pos + 1 && format.at(pos + 1) == '}') { - // tprintf(os, format.substr(pos + 2), Fargs...); // recursive call - // } else { - // tprintf(os, format.substr(pos + 1), Fargs...); // recursive call - // } - // return; - // } - // os << c; - // pos++; - // } - // } + template + void tprintf(std::stringstream& os, const std::string& format, std::int8_t value, Targs... Fargs) // recursive variadic function + { + std::size_t pos = 0; + for ( auto c : format ) { + if ( c == '{' ) { + os << std::int16_t(value); + if (format.size() > pos + 1 && format.at(pos + 1) == '}') { + tprintf(os, format.substr(pos + 2), Fargs...); // recursive call + } else { + tprintf(os, format.substr(pos + 1), Fargs...); // recursive call + } + return; + } + os << c; + ++pos; + } + } // template // void tprintf(std::stringstream& os, const std::string& format, const std::string& value, Targs... Fargs) // recursive variadic function @@ -205,10 +205,15 @@ namespace { for ( auto c : format ) { if ( c == '{' ) { os << value; + if (format.size() > pos + 1 && format.at(pos + 1) == '}') { + tprintf(os, format.substr(pos + 2)); // recursive call + } else { + tprintf(os, format.substr(pos + 1)); // recursive call + } return; } os << c; - pos++; + ++pos; } } @@ -227,7 +232,7 @@ namespace { return; } os << c; - pos++; + ++pos; } } diff --git a/herald/include/herald/engine/coordinator.h b/herald/include/herald/engine/coordinator.h index dbccb23..1497550 100644 --- a/herald/include/herald/engine/coordinator.h +++ b/herald/include/herald/engine/coordinator.h @@ -2,13 +2,20 @@ // SPDX-License-Identifier: Apache-2.0 // -#ifndef COORDINATOR_H -#define COORDINATOR_H +#ifndef HERALD_COORDINATOR_H +#define HERALD_COORDINATOR_H #include "../context.h" #include "../sensor.h" +#include "activities.h" +#include "../data/sensor_logger.h" #include +#include +#include +#include +#include +#include namespace herald { @@ -34,24 +41,174 @@ template class Coordinator { public: /// Default constructor. Receives a configured platform-specific context instance. - Coordinator(ContextT& context); - ~Coordinator(); + Coordinator(ContextT& ctx) + : context(ctx), + providers(), + running(false) + HLOGGERINIT(ctx,"engine","coordinator") + {} + + ~Coordinator() = default; /// Introspect and include in iteration planning - void add(std::shared_ptr sensor); + void add(std::shared_ptr sensor) { + HTDBG("Adding sensor"); + auto prov = sensor->coordinationProvider(); + if (prov.has_value()) { + HTDBG("Sensor has Provider implementation"); + providers.push_back(prov.value()); + } + } /// Remove from iteration planning - void remove(std::shared_ptr sensor); + void remove(std::shared_ptr sensor) + { + // TODO support remove + } /// Prepares for iterations to be called (may pre-emptively make calls) - void start(); + void start() { + HTDBG("Start called"); + // Clear feature providers + featureProviders.clear(); + // Fetch feature providers + for (auto prov: providers) { + auto myFeatures = prov->connectionsProvided(); + for (auto feature : myFeatures) { + featureProviders.emplace(feature,prov); + } + } + running = true; + HTDBG("Start returning"); + } + /// Execute an iteration of activity, according to settings - void iteration(); + void iteration() { + if (!running) { + HTDBG("Coordinator not running. Returning from iteration having done nothing."); + return; + } + HTDBG("################# ITERATION #################"); + // HTDBG("Entered iteration"); + // Create empty list of required prereqs per provider + std::map,std::vector> assignPrereqs; + for (auto& prov : providers) { + assignPrereqs.emplace(prov,std::vector()); + } + // HTDBG("Completed initialisation of provider prerequisities containers"); + // HTDBG(" - Provider count: {}", providers.size()); + + std::vector connsRequired; + // Loop over providers and ask for feature pre-requisites + for (auto& prov : providers) { + auto myConns = prov->requiredConnections(); + std::copy(myConns.begin(),myConns.end(), + std::back_insert_iterator>(connsRequired)); + } + // HTDBG(std::to_string(connsRequired.size())); + // HTDBG("Retrieved providers' current prerequisites"); + // TODO de-duplicate pre-reqs + // Now link required prereqs to each provider + for (auto& p : connsRequired) { + auto el = featureProviders.find(std::get<0>(p)); // find provider for given prereq by feature tag + if (featureProviders.end() != el) { + assignPrereqs[el->second].push_back(p); + } + } + // HTDBG("Linked pre-reqs to their providers"); + + // // Some debug checks here + // int cnt = 0; + // for (auto& ass : assignPrereqs) { + // // HTDBG("assign prereqs number {} has this many prereqs to fill {}", cnt, ass.second.size()); + // cnt++; + // } + + // Communicate with relevant feature providers and request features for targets (in descending priority order) + // - Includes removal of previous features no longer needed + std::vector provisioned; + for (auto& prov : assignPrereqs) { + // TODO sort by descending priority before passing on + + // FOR PLATFORMS WITH STD::FUTURE AND STD::ASYNC + // std::future fut = std::async(std::launch::async, + // &CoordinationProvider::provision, prov.first, + // //prov.first->provision( + // prov.second,[&provisioned] ( + // const std::vector myProvisioned) -> void { + // std::copy(myProvisioned.begin(),myProvisioned.end(), + // std::back_insert_iterator>(provisioned)); + // }); + // fut.get(); // waits for callback // TODO wait with timeout + + // FOR OTHER PLATFORMS (E.g. ZEPHYR):- + std::vector myProvisioned = prov.first->provision(prov.second); + std::copy(myProvisioned.begin(),myProvisioned.end(), + std::back_insert_iterator>(provisioned)); + } + // HTDBG("All pre-requisities requests sent and responses received"); + // TODO do the above asynchronously and await callback or timeout for all + + // For each which are now present, ask for activities (in descending priority order) + for (auto& prov : providers) { + auto maxActs = prov->requiredActivities(); + // TODO sort by descending priority before actioning + for (auto& act : maxActs) { + std::string san("Activity "); + san += act.name; + HTDBG(san); + // HTDBG("Checking next desired activity for prereqs being satisfied"); + // Filter requested by provisioned + bool allFound = true; + for (auto& pre : act.prerequisites) { + bool myFound = false; + for (auto& exists : provisioned) { + if (std::get<0>(pre) == std::get<0>(exists) && + std::get<1>(pre) == std::get<2>(exists)) { + myFound = true; + } + } + allFound = allFound & myFound; + if (myFound) { + HTDBG(" - Prereq satisfied"); + } else { + HTDBG(" - Prereq NOT SATISFIED"); + } + } + // Carry out activities with completion callbacks passed in + if (allFound) { + HTDBG("All satisfied, calling activity"); + // do activity + + // FOR PLATFORMS WITH STD::ASYNC + // act.executor(act,[this] (Activity act, std::optional followOn) -> void { + // // TODO handle result + // // TODO Carry out any follow up activities + // HTDBG("Activity completion callback called"); + // }); + + // FOR PLATFORMS WITHOUT + std::optional followOn = act.executor(act); + // TODO carry out follow on activity until no more follow ons (or max follow on number hit) + } + } + } + // HTDBG("Leaving iteration"); + HTDBG("################# END #################"); + } /// Closes out any existing connections/activities - void stop(); + void stop() { + running = false; + } private: - class Impl; - std::unique_ptr mImpl; + ContextT& context; + + std::vector> providers; + std::map> featureProviders; + + bool running; + + HLOGGER(ContextT); }; /** Comparator for less than (use in maps) **/ diff --git a/herald/src/ble/ble_coordinator.cpp b/herald/src/ble/ble_coordinator.cpp index 960752c..3510298 100644 --- a/herald/src/ble/ble_coordinator.cpp +++ b/herald/src/ble/ble_coordinator.cpp @@ -16,423 +16,412 @@ namespace ble { using namespace herald::engine; -template -class HeraldProtocolBLECoordinationProvider::Impl { -public: - Impl(ContextT& ctx, std::shared_ptr bledb,std::shared_ptr provider); - ~Impl(); - - ContextT& context; - std::shared_ptr db; - std::shared_ptr pp; - - std::vector previouslyProvisioned; - - int iterationsSinceBreak; - int breakEvery; - int breakFor; - - HLOGGER(ContextT); -}; - -template -HeraldProtocolBLECoordinationProvider::Impl::Impl(ContextT& ctx, std::shared_ptr bledb,std::shared_ptr provider) - : context(ctx), - db(bledb), - pp(provider), - previouslyProvisioned(), - iterationsSinceBreak(0), - breakEvery(10), - breakFor(10) - HLOGGERINIT(ctx,"heraldble","coordinationprovider") -{ - ; -} - -template -HeraldProtocolBLECoordinationProvider::Impl::~Impl() -{ - ; -} - - - -template -HeraldProtocolBLECoordinationProvider::HeraldProtocolBLECoordinationProvider( - ContextT& ctx, - std::shared_ptr db, std::shared_ptr provider) - : mImpl(std::make_unique(ctx,db,provider)) -{ - ; -} - - -template -HeraldProtocolBLECoordinationProvider::~HeraldProtocolBLECoordinationProvider() -{ - ; -} - - -template -std::vector -HeraldProtocolBLECoordinationProvider::connectionsProvided() -{ - return std::vector(1,herald::engine::Features::HeraldBluetoothProtocolConnection); -} +// template +// class HeraldProtocolBLECoordinationProvider::Impl { +// public: +// Impl(ContextT& ctx, std::shared_ptr bledb,std::shared_ptr provider); +// ~Impl(); + +// }; + +// template +// HeraldProtocolBLECoordinationProvider::Impl::Impl(ContextT& ctx, std::shared_ptr bledb,std::shared_ptr provider) +// : context(ctx), +// db(bledb), +// pp(provider), +// previouslyProvisioned(), +// iterationsSinceBreak(0), +// breakEvery(10), +// breakFor(10) +// HLOGGERINIT(ctx,"heraldble","coordinationprovider") +// { +// ; +// } + +// template +// HeraldProtocolBLECoordinationProvider::Impl::~Impl() +// { +// ; +// } + + + +// template +// HeraldProtocolBLECoordinationProvider::HeraldProtocolBLECoordinationProvider( +// ContextT& ctx, +// std::shared_ptr db, std::shared_ptr provider) +// : mImpl(std::make_unique(ctx,db,provider)) +// { +// ; +// } + + +// template +// HeraldProtocolBLECoordinationProvider::~HeraldProtocolBLECoordinationProvider() +// { +// ; +// } + + +// template +// std::vector +// HeraldProtocolBLECoordinationProvider::connectionsProvided() +// { +// return std::vector(1,herald::engine::Features::HeraldBluetoothProtocolConnection); +// } // void // HeraldProtocolBLECoordinationProvider::provision(const std::vector& requested, // const ConnectionCallback& connCallback) -template -std::vector -HeraldProtocolBLECoordinationProvider::provision( - const std::vector& requested) -{ - if (requested.empty()) { - // HDBG("No connections requested for provisioning"); - } else { - HDBG("Provisioning connections"); - } - - // Remove those previously provisoned that we no longer require - for (auto& previous : mImpl->previouslyProvisioned) { - bool found = false; - for (auto& req : requested) { - found = found || std::get<2>(req)==std::get<2>(previous); // comparing target values - } - if (!found) { - HDBG(" - Found connection to remove"); - auto& optTarget = std::get<2>(previous); - // We can't disconnect from 'all', so check for target - if (optTarget.has_value()) { - // Ignoring closed true/false result - mImpl->pp->closeConnection(optTarget.value()); - } - } - } - - // Now provision new connections - std::vector provisioned; - // For this provider, a prerequisite is a connection to a remote target identifier over Bluetooth - auto requestIter = requested.cbegin(); - bool lastConnectionSuccessful = true; - while (/*mImpl->currentConnections < mImpl->maxConnections && */ - lastConnectionSuccessful && - requestIter != requested.cend()) { - HDBG(" - Satisfying prereq"); - // HDBG(" - currentConnections currently:-"); - // HDBG(std::to_string(mImpl->currentConnections)); - auto& req = *requestIter; - // See if we're already connected - // If so, add to provisioned list - // If not, try to connect - auto& optTarget = std::get<2>(req); - if (optTarget.has_value()) { - HDBG(" - Have defined target for this prerequisite. Requesting connection be made available."); - // std::future fut = std::async(std::launch::async, - // &HeraldProtocolV1Provider::openConnection,mImpl->pp, - // optTarget.value(),[&lastConnectionSuccessful] ( - // const TargetIdentifier& targetForConnection, bool success) -> void { - // lastConnectionSuccessful = success; - // }); - // fut.get(); // TODO FIND OUT HOW TO DO THIS FUTURE WAITING FUNCTIONALITY - - lastConnectionSuccessful = mImpl->pp->openConnection(optTarget.value()); - - // If successful, add to provisioned list - if (lastConnectionSuccessful) { - HDBG(" - Ensuring connection successful"); - provisioned.push_back(req); - // mImpl->currentConnections++; - } else { - HDBG(" - Ensuring connection UNSUCCESSFUL"); - } - } else { - HDBG(" - No defined target, returning satisfied - always true for Herald BLE"); - // if target not specified then it just means any open connection, so return OK - provisioned.push_back(req); - // TODO determine what to do here if sensor stopped, bluetooth disabled, or no connections open - } - // move forward in iterator - requestIter++; - - lastConnectionSuccessful = true; - } - - mImpl->previouslyProvisioned = provisioned; - - // TODO schedule disconnection from not required items (E.g. after minimum connection time) - // - we already do this if provision is called, but not after a time period - // - Bluetooth timeout will deal with the underlying connection, but not any intermediate state in the PP instance - - // HDBG("Returning from provision"); - // connCallback(provisioned); - return provisioned; -} - - - -template -std::vector>> -HeraldProtocolBLECoordinationProvider::requiredConnections() -{ - - std::vector>> results; - - // This ensures we break from making connections to allow advertising and scanning - mImpl->iterationsSinceBreak++; - if (mImpl->iterationsSinceBreak >= mImpl->breakEvery && - mImpl->iterationsSinceBreak < (mImpl->breakEvery + mImpl->breakFor) ) { - HDBG("###### Skipping connections - giving advertising & scanning a chance"); - // if (mImpl->iterationsSinceBreak == mImpl->breakEvery) { // incase it fails - mImpl->pp->restartScanningAndAdvertising(); - // } - return results; - } else if (mImpl->iterationsSinceBreak == (mImpl->breakEvery + mImpl->breakFor) ) { - // reset - mImpl->iterationsSinceBreak = 0; - } - - // Remove expired devices - auto expired = mImpl->db->matches([this] (std::shared_ptr& device) -> bool { - auto interval = device->timeIntervalSinceLastUpdate(); - bool notZero = interval != TimeInterval::zero(); - bool isOld = interval > TimeInterval::minutes(15); - // HDBG("ID, created, Now, interval, notZero, isOld:-"); - // HDBG((std::string)device->identifier()); - // HDBG(std::to_string((long)device->created())); - // HDBG(std::to_string((long)Date())); - // HDBG((std::string)interval); - // HDBG(notZero?"true":"false"); - // HDBG(isOld?"true":"false"); - return notZero && isOld; - }); - for (auto& exp : expired) { - mImpl->db->remove(exp->identifier()); - HDBG("Removing expired device with ID: "); - HDBG((std::string)exp->identifier()); - HDBG("time since last update:-"); - HDBG(std::to_string(exp->timeIntervalSinceLastUpdate())); - } - - // Allow updates from ignored (for a time) status, to retry status - auto tempIgnoredOS = mImpl->db->matches([](std::shared_ptr& device) -> bool { - return device->operatingSystem() == BLEDeviceOperatingSystem::ignore; - }); - for (auto& device : tempIgnoredOS) { - // don't bother with separate activity right now - no connection required - device->operatingSystem(BLEDeviceOperatingSystem::unknown); - } - - - // Add all targets in database that are not known - auto newConns = mImpl->db->matches([](std::shared_ptr& device) -> bool { - return !device->ignore() && - ( - !device->hasService(BLESensorConfiguration::serviceUUID) - || - !device->payloadData().has_value() // Know the OS, but not the payload (ID) - || - device->immediateSendData().has_value() - ) - ; - }); - for (auto& device : newConns) { - results.emplace_back(herald::engine::Features::HeraldBluetoothProtocolConnection, - herald::engine::Priorities::High, - device->identifier() - ); - } - - // TODO any other devices we may have outstanding work for that requires connections - - // DEBUG ONLY ELEMENTS - if (newConns.size() > 0) { - // print debug info about the BLE Database - HDBG("BLE DATABASE CURRENT CONTENTS:-"); - auto allDevices = mImpl->db->matches([](std::shared_ptr& device) -> bool { - return true; - }); - for (auto& device : allDevices) { - std::string di(" - "); - BLEMacAddress mac((Data)device->identifier()); - di += (std::string)mac; - di += ", created="; - di += std::to_string(device->created()); - di += ", pseudoAddress="; - auto pseudo = device->pseudoDeviceAddress(); - if (pseudo.has_value()) { - di += (std::string)pseudo.value(); - } else { - di += "unset"; - } - di += ", os="; - auto os = device->operatingSystem(); - if (os.has_value()) { - if (herald::ble::BLEDeviceOperatingSystem::ios == os) { - di += "ios"; - } else if (herald::ble::BLEDeviceOperatingSystem::android == os) { - di += "android"; - } else if (herald::ble::BLEDeviceOperatingSystem::unknown == os) { - di += "unknown"; - } else if (herald::ble::BLEDeviceOperatingSystem::ignore == os) { - di += "ignore"; - } else if (herald::ble::BLEDeviceOperatingSystem::android_tbc == os) { - di += "android_tbc"; - } else if (herald::ble::BLEDeviceOperatingSystem::ios_tbc == os) { - di += "ios_tbc"; - } else if (herald::ble::BLEDeviceOperatingSystem::shared == os) { - di += "shared"; - } - } else { - di += "unknown/unset"; - } - di += ", ignore="; - auto ignore = device->ignore(); - if (ignore) { - di += "true (for "; - di += std::to_string(device->timeIntervalUntilIgnoreExpired().millis()); - di += " more secs)"; - } else { - di += "false"; - } - di += ", hasServices="; - di += (device->hasServicesSet() ? "true" : "false"); - di += ", hasReadPayload="; - di += (device->payloadData().has_value() ? device->payloadData().value().hexEncodedString() : "false"); - HDBG(di); - } - } else { - // restart scanning when no connection activity is expected - mImpl->pp->restartScanningAndAdvertising(); - } - - return results; -} - -template -std::vector -HeraldProtocolBLECoordinationProvider::requiredActivities() -{ - std::vector results; - - // State 0 - New device -> Read full advert data to see if DCT/Herald -> State Z, 1 or State 3 - // State 1 - Discover services for DCT/Herald on this device -> State Z, or 2 - // State 2 - New Herald BLE device -> Read payload -> State 3 - // State 3 - Steady state - do nothing - // State 4 - Has immediateSend data -> Send immediate -> State 3 - // TODO check for nearby payloads - // State 3 - Not seen in a while -> State X - // State X - Out of range. No actions. - // State Z - Ignore (not a relevant device for Herald... right now). Ignore for a period of time. No actions. - - // TODO is IOS and needs payload sharing +// template +// std::vector +// HeraldProtocolBLECoordinationProvider::provision( +// const std::vector& requested) +// { +// if (requested.empty()) { +// // HDBG("No connections requested for provisioning"); +// } else { +// HDBG("Provisioning connections"); +// } + +// // Remove those previously provisoned that we no longer require +// for (auto& previous : mImpl->previouslyProvisioned) { +// bool found = false; +// for (auto& req : requested) { +// found = found || std::get<2>(req)==std::get<2>(previous); // comparing target values +// } +// if (!found) { +// HDBG(" - Found connection to remove"); +// auto& optTarget = std::get<2>(previous); +// // We can't disconnect from 'all', so check for target +// if (optTarget.has_value()) { +// // Ignoring closed true/false result +// mImpl->pp->closeConnection(optTarget.value()); +// } +// } +// } + +// // Now provision new connections +// std::vector provisioned; +// // For this provider, a prerequisite is a connection to a remote target identifier over Bluetooth +// auto requestIter = requested.cbegin(); +// bool lastConnectionSuccessful = true; +// while (/*mImpl->currentConnections < mImpl->maxConnections && */ +// lastConnectionSuccessful && +// requestIter != requested.cend()) { +// HDBG(" - Satisfying prereq"); +// // HDBG(" - currentConnections currently:-"); +// // HDBG(std::to_string(mImpl->currentConnections)); +// auto& req = *requestIter; +// // See if we're already connected +// // If so, add to provisioned list +// // If not, try to connect +// auto& optTarget = std::get<2>(req); +// if (optTarget.has_value()) { +// HDBG(" - Have defined target for this prerequisite. Requesting connection be made available."); +// // std::future fut = std::async(std::launch::async, +// // &HeraldProtocolV1Provider::openConnection,mImpl->pp, +// // optTarget.value(),[&lastConnectionSuccessful] ( +// // const TargetIdentifier& targetForConnection, bool success) -> void { +// // lastConnectionSuccessful = success; +// // }); +// // fut.get(); // TODO FIND OUT HOW TO DO THIS FUTURE WAITING FUNCTIONALITY + +// lastConnectionSuccessful = mImpl->pp->openConnection(optTarget.value()); + +// // If successful, add to provisioned list +// if (lastConnectionSuccessful) { +// HDBG(" - Ensuring connection successful"); +// provisioned.push_back(req); +// // mImpl->currentConnections++; +// } else { +// HDBG(" - Ensuring connection UNSUCCESSFUL"); +// } +// } else { +// HDBG(" - No defined target, returning satisfied - always true for Herald BLE"); +// // if target not specified then it just means any open connection, so return OK +// provisioned.push_back(req); +// // TODO determine what to do here if sensor stopped, bluetooth disabled, or no connections open +// } +// // move forward in iterator +// requestIter++; + +// lastConnectionSuccessful = true; +// } + +// mImpl->previouslyProvisioned = provisioned; + +// // TODO schedule disconnection from not required items (E.g. after minimum connection time) +// // - we already do this if provision is called, but not after a time period +// // - Bluetooth timeout will deal with the underlying connection, but not any intermediate state in the PP instance + +// // HDBG("Returning from provision"); +// // connCallback(provisioned); +// return provisioned; +// } + + + +// template +// std::vector>> +// HeraldProtocolBLECoordinationProvider::requiredConnections() +// { + +// std::vector>> results; + +// // This ensures we break from making connections to allow advertising and scanning +// mImpl->iterationsSinceBreak++; +// if (mImpl->iterationsSinceBreak >= mImpl->breakEvery && +// mImpl->iterationsSinceBreak < (mImpl->breakEvery + mImpl->breakFor) ) { +// HDBG("###### Skipping connections - giving advertising & scanning a chance"); +// // if (mImpl->iterationsSinceBreak == mImpl->breakEvery) { // incase it fails +// mImpl->pp->restartScanningAndAdvertising(); +// // } +// return results; +// } else if (mImpl->iterationsSinceBreak == (mImpl->breakEvery + mImpl->breakFor) ) { +// // reset +// mImpl->iterationsSinceBreak = 0; +// } + +// // Remove expired devices +// auto expired = mImpl->db->matches([this] (std::shared_ptr& device) -> bool { +// auto interval = device->timeIntervalSinceLastUpdate(); +// bool notZero = interval != TimeInterval::zero(); +// bool isOld = interval > TimeInterval::minutes(15); +// // HDBG("ID, created, Now, interval, notZero, isOld:-"); +// // HDBG((std::string)device->identifier()); +// // HDBG(std::to_string((long)device->created())); +// // HDBG(std::to_string((long)Date())); +// // HDBG((std::string)interval); +// // HDBG(notZero?"true":"false"); +// // HDBG(isOld?"true":"false"); +// return notZero && isOld; +// }); +// for (auto& exp : expired) { +// mImpl->db->remove(exp->identifier()); +// HDBG("Removing expired device with ID: "); +// HDBG((std::string)exp->identifier()); +// HDBG("time since last update:-"); +// HDBG(std::to_string(exp->timeIntervalSinceLastUpdate())); +// } + +// // Allow updates from ignored (for a time) status, to retry status +// auto tempIgnoredOS = mImpl->db->matches([](std::shared_ptr& device) -> bool { +// return device->operatingSystem() == BLEDeviceOperatingSystem::ignore; +// }); +// for (auto& device : tempIgnoredOS) { +// // don't bother with separate activity right now - no connection required +// device->operatingSystem(BLEDeviceOperatingSystem::unknown); +// } + + +// // Add all targets in database that are not known +// auto newConns = mImpl->db->matches([](std::shared_ptr& device) -> bool { +// return !device->ignore() && +// ( +// !device->hasService(BLESensorConfiguration::serviceUUID) +// || +// !device->payloadData().has_value() // Know the OS, but not the payload (ID) +// || +// device->immediateSendData().has_value() +// ) +// ; +// }); +// for (auto& device : newConns) { +// results.emplace_back(herald::engine::Features::HeraldBluetoothProtocolConnection, +// herald::engine::Priorities::High, +// device->identifier() +// ); +// } + +// // TODO any other devices we may have outstanding work for that requires connections + +// // DEBUG ONLY ELEMENTS +// if (newConns.size() > 0) { +// // print debug info about the BLE Database +// HDBG("BLE DATABASE CURRENT CONTENTS:-"); +// auto allDevices = mImpl->db->matches([](std::shared_ptr& device) -> bool { +// return true; +// }); +// for (auto& device : allDevices) { +// std::string di(" - "); +// BLEMacAddress mac((Data)device->identifier()); +// di += (std::string)mac; +// di += ", created="; +// di += std::to_string(device->created()); +// di += ", pseudoAddress="; +// auto pseudo = device->pseudoDeviceAddress(); +// if (pseudo.has_value()) { +// di += (std::string)pseudo.value(); +// } else { +// di += "unset"; +// } +// di += ", os="; +// auto os = device->operatingSystem(); +// if (os.has_value()) { +// if (herald::ble::BLEDeviceOperatingSystem::ios == os) { +// di += "ios"; +// } else if (herald::ble::BLEDeviceOperatingSystem::android == os) { +// di += "android"; +// } else if (herald::ble::BLEDeviceOperatingSystem::unknown == os) { +// di += "unknown"; +// } else if (herald::ble::BLEDeviceOperatingSystem::ignore == os) { +// di += "ignore"; +// } else if (herald::ble::BLEDeviceOperatingSystem::android_tbc == os) { +// di += "android_tbc"; +// } else if (herald::ble::BLEDeviceOperatingSystem::ios_tbc == os) { +// di += "ios_tbc"; +// } else if (herald::ble::BLEDeviceOperatingSystem::shared == os) { +// di += "shared"; +// } +// } else { +// di += "unknown/unset"; +// } +// di += ", ignore="; +// auto ignore = device->ignore(); +// if (ignore) { +// di += "true (for "; +// di += std::to_string(device->timeIntervalUntilIgnoreExpired().millis()); +// di += " more secs)"; +// } else { +// di += "false"; +// } +// di += ", hasServices="; +// di += (device->hasServicesSet() ? "true" : "false"); +// di += ", hasReadPayload="; +// di += (device->payloadData().has_value() ? device->payloadData().value().hexEncodedString() : "false"); +// HDBG(di); +// } +// } else { +// // restart scanning when no connection activity is expected +// mImpl->pp->restartScanningAndAdvertising(); +// } + +// return results; +// } + +// template +// std::vector +// HeraldProtocolBLECoordinationProvider::requiredActivities() +// { +// std::vector results; + +// // State 0 - New device -> Read full advert data to see if DCT/Herald -> State Z, 1 or State 3 +// // State 1 - Discover services for DCT/Herald on this device -> State Z, or 2 +// // State 2 - New Herald BLE device -> Read payload -> State 3 +// // State 3 - Steady state - do nothing +// // State 4 - Has immediateSend data -> Send immediate -> State 3 +// // TODO check for nearby payloads +// // State 3 - Not seen in a while -> State X +// // State X - Out of range. No actions. +// // State Z - Ignore (not a relevant device for Herald... right now). Ignore for a period of time. No actions. + +// // TODO is IOS and needs payload sharing - // auto state0Devices = mImpl->db->matches([](std::shared_ptr device) -> bool { - // return !device->ignore() && !device->pseudoDeviceAddress().has_value(); - // }); - auto state1Devices = mImpl->db->matches([](std::shared_ptr& device) -> bool { - return !device->ignore() && - !device->receiveOnly() && - !device->hasService(BLESensorConfiguration::serviceUUID); - }); - auto state2Devices = mImpl->db->matches([](std::shared_ptr& device) -> bool { - return !device->ignore() && - !device->receiveOnly() && - device->hasService(BLESensorConfiguration::serviceUUID) && - !device->payloadData().has_value(); // TODO check for Herald transferred payload data (not legacy) - }); - auto state4Devices = mImpl->db->matches([](std::shared_ptr& device) -> bool { - return !device->ignore() && - !device->receiveOnly() && - device->hasService(BLESensorConfiguration::serviceUUID) && - device->immediateSendData().has_value(); - }); - // TODO State X (timed out / out of range) devices filter check -> Then remove from BLEDatabase +// // auto state0Devices = mImpl->db->matches([](std::shared_ptr device) -> bool { +// // return !device->ignore() && !device->pseudoDeviceAddress().has_value(); +// // }); +// auto state1Devices = mImpl->db->matches([](std::shared_ptr& device) -> bool { +// return !device->ignore() && +// !device->receiveOnly() && +// !device->hasService(BLESensorConfiguration::serviceUUID); +// }); +// auto state2Devices = mImpl->db->matches([](std::shared_ptr& device) -> bool { +// return !device->ignore() && +// !device->receiveOnly() && +// device->hasService(BLESensorConfiguration::serviceUUID) && +// !device->payloadData().has_value(); // TODO check for Herald transferred payload data (not legacy) +// }); +// auto state4Devices = mImpl->db->matches([](std::shared_ptr& device) -> bool { +// return !device->ignore() && +// !device->receiveOnly() && +// device->hasService(BLESensorConfiguration::serviceUUID) && +// device->immediateSendData().has_value(); +// }); +// // TODO State X (timed out / out of range) devices filter check -> Then remove from BLEDatabase - // NOTE State 0 is handled by the Herald BLE scan function, and so has no specific activity - - // State 1 - discovery Herald service - for (auto& device : state1Devices) { - results.emplace_back(Activity{ - .priority = Priorities::High + 10, - .name = "herald-service-discovery", - .prerequisites = std::vector>>{ - 1, - std::tuple>{ - herald::engine::Features::HeraldBluetoothProtocolConnection, - device->identifier() - } - }, - // .executor = [this](const Activity activity, CompletionCallback callback) -> void { - // // fill this out - // mImpl->pp->serviceDiscovery(activity,callback); - // } - .executor = [this](const Activity activity) -> std::optional { - // fill this out - mImpl->pp->serviceDiscovery(activity); - return {}; - } - }); - } - - // State 2 - read herald payload(s) - for (auto& device : state2Devices) { - results.emplace_back(Activity{ - .priority = Priorities::High + 9, - .name = "herald-read-payload", - .prerequisites = std::vector>>{ - 1, - std::tuple>{ - herald::engine::Features::HeraldBluetoothProtocolConnection, - device->identifier() - } - }, - // .executor = [this](const Activity activity, CompletionCallback callback) -> void { - // // fill this out - // mImpl->pp->readPayload(activity,callback); - // } - .executor = [this](const Activity activity) -> std::optional { - // fill this out - mImpl->pp->readPayload(activity); - return {}; - } - }); - } - // TODO add check for sensor config payload timeout in above IF - // TODO add BLESensorConfiguration.deviceIntrospectionEnabled && device.supportsModelCharacteristic() && device.model() == null - // TODO add BLESensorConfiguration.deviceIntrospectionEnabled && device.supportsDeviceNameCharacteristic() && device.deviceName() == null +// // NOTE State 0 is handled by the Herald BLE scan function, and so has no specific activity + +// // State 1 - discovery Herald service +// for (auto& device : state1Devices) { +// results.emplace_back(Activity{ +// .priority = Priorities::High + 10, +// .name = "herald-service-discovery", +// .prerequisites = std::vector>>{ +// 1, +// std::tuple>{ +// herald::engine::Features::HeraldBluetoothProtocolConnection, +// device->identifier() +// } +// }, +// // .executor = [this](const Activity activity, CompletionCallback callback) -> void { +// // // fill this out +// // mImpl->pp->serviceDiscovery(activity,callback); +// // } +// .executor = [this](const Activity activity) -> std::optional { +// // fill this out +// mImpl->pp->serviceDiscovery(activity); +// return {}; +// } +// }); +// } + +// // State 2 - read herald payload(s) +// for (auto& device : state2Devices) { +// results.emplace_back(Activity{ +// .priority = Priorities::High + 9, +// .name = "herald-read-payload", +// .prerequisites = std::vector>>{ +// 1, +// std::tuple>{ +// herald::engine::Features::HeraldBluetoothProtocolConnection, +// device->identifier() +// } +// }, +// // .executor = [this](const Activity activity, CompletionCallback callback) -> void { +// // // fill this out +// // mImpl->pp->readPayload(activity,callback); +// // } +// .executor = [this](const Activity activity) -> std::optional { +// // fill this out +// mImpl->pp->readPayload(activity); +// return {}; +// } +// }); +// } +// // TODO add check for sensor config payload timeout in above IF +// // TODO add BLESensorConfiguration.deviceIntrospectionEnabled && device.supportsModelCharacteristic() && device.model() == null +// // TODO add BLESensorConfiguration.deviceIntrospectionEnabled && device.supportsDeviceNameCharacteristic() && device.deviceName() == null - // State 4 - Has data for immediate send - for (auto& device : state4Devices) { - results.emplace_back(Activity{ - .priority = Priorities::Default + 10, - .name = "herald-immediate-send-targeted", - .prerequisites = std::vector>>{ - 1, - std::tuple>{ - herald::engine::Features::HeraldBluetoothProtocolConnection, - device->identifier() - } - }, - // For std::async based platforms:- - // .executor = [this](const Activity activity, CompletionCallback callback) -> void { - // // fill this out - // mImpl->pp->immediateSend(activity,callback); - // } - .executor = [this](const Activity activity) -> std::optional { - // fill this out - mImpl->pp->immediateSend(activity); - return {}; - } - }); - // TODO add immediate send all support - // TODO add read of nearby payload data from remotes - } - return results; -} +// // State 4 - Has data for immediate send +// for (auto& device : state4Devices) { +// results.emplace_back(Activity{ +// .priority = Priorities::Default + 10, +// .name = "herald-immediate-send-targeted", +// .prerequisites = std::vector>>{ +// 1, +// std::tuple>{ +// herald::engine::Features::HeraldBluetoothProtocolConnection, +// device->identifier() +// } +// }, +// // For std::async based platforms:- +// // .executor = [this](const Activity activity, CompletionCallback callback) -> void { +// // // fill this out +// // mImpl->pp->immediateSend(activity,callback); +// // } +// .executor = [this](const Activity activity) -> std::optional { +// // fill this out +// mImpl->pp->immediateSend(activity); +// return {}; +// } +// }); +// // TODO add immediate send all support +// // TODO add read of nearby payload data from remotes +// } +// return results; +// } } diff --git a/herald/src/ble/concrete_ble_database.cpp b/herald/src/ble/concrete_ble_database.cpp index 9fe69c1..7eac3de 100644 --- a/herald/src/ble/concrete_ble_database.cpp +++ b/herald/src/ble/concrete_ble_database.cpp @@ -19,351 +19,5 @@ namespace herald { namespace ble { -using namespace herald::datatype; -using namespace herald::ble::filter; - -struct last_updated_descending { - bool operator()(const std::shared_ptr& a, const std::shared_ptr& b) { - return a->timeIntervalSinceLastUpdate() > b->timeIntervalSinceLastUpdate(); // opposite order - } -}; - - - - - -template -class ConcreteBLEDatabase::Impl { -public: - Impl(ContextT& context); - ~Impl(); - - void assignAdvertData(std::shared_ptr& newDevice, std::vector&& toMove, const std::vector& manuData); - - ContextT& ctx; - std::vector> delegates; - std::vector> devices; - - HLOGGER(ContextT); -}; - -template -ConcreteBLEDatabase::Impl::Impl(ContextT& context) - : ctx(context), - delegates(), - devices() - HLOGGERINIT(ctx,"herald","ConcreteBLEDatabase") -{ - ; -} - -template -ConcreteBLEDatabase::Impl::~Impl() -{ - ; -} - -template -void -ConcreteBLEDatabase::Impl::assignAdvertData(std::shared_ptr& newDevice, std::vector&& toMove, const std::vector& manuData) -{ - newDevice->advertData(std::move(toMove)); - - // If it's an apple device, check to see if its on our ignore list - auto appleDataSegments = BLEAdvertParser::extractAppleManufacturerSegments(manuData); - if (0 != appleDataSegments.size()) { - HTDBG("Found apple device"); - // HTDBG((std::string)mac); - newDevice->operatingSystem(BLEDeviceOperatingSystem::ios); - // TODO see if we should ignore this Apple device - // TODO abstract these out eventually in to BLEDevice class - bool ignore = false; - /* - "^10....04", - "^10....14", - "^0100000000000000000000000000000000", - "^05","^07","^09", - "^00","^1002","^06","^08","^03","^0C","^0D","^0F","^0E","^0B" - */ - for (auto& segment : appleDataSegments) { - HTDBG(segment.data.hexEncodedString()); - switch (segment.type) { - case 0x00: - case 0x05: - case 0x07: - case 0x09: - case 0x06: - case 0x08: - case 0x03: - case 0x0C: - case 0x0D: - case 0x0F: - case 0x0E: - case 0x0B: - ignore = true; - break; - case 0x10: - // check if second is 02 - if (segment.data.at(0) == std::byte(0x02)) { - ignore = true; - } else { - // Check 3rd data bit for 14 or 04 - if (segment.data.at(2) == std::byte(0x04) || segment.data.at(2) == std::byte(0x14)) { - ignore = true; - } - } - break; - default: - break; - } - } - if (ignore) { - HTDBG(" - Ignoring Apple device due to Apple data filter"); - newDevice->ignore(true); - } else { - // Perform GATT service discovery to check for Herald service - // NOTE: Happens from Connection request (handled by BLE Coordinator) - HTDBG(" - Unknown apple device... Logging so we can discover services later"); - } - } else { - // Not a Herald android or any iOS - so inspect later (beacon or wearable) - HTDBG("Unknown non Herald device - inspecting (might be a venue beacon or wearable)"); - // HTDBG((std::string)mac); - } -} - - - - - -template -ConcreteBLEDatabase::ConcreteBLEDatabase(ContextT& context) - : mImpl(std::make_unique(context)) -{ - ; -} - -template -ConcreteBLEDatabase::~ConcreteBLEDatabase() -{ - ; -} - -// BLE Database overrides - -template -void -ConcreteBLEDatabase::add(const std::shared_ptr& delegate) -{ - mImpl->delegates.push_back(delegate); -} - -template -std::shared_ptr -ConcreteBLEDatabase::device(const PayloadData& payloadData) -{ - auto results = matches([&payloadData](const std::shared_ptr& d) { - auto payload = d->payloadData(); - if (!payload.has_value()) { - return false; - } - return (*payload)==payloadData; - }); - if (results.size() != 0) { - return results.front(); // TODO ensure we send back the latest, not just the first match - } - std::shared_ptr newDevice = std::make_shared( - TargetIdentifier(payloadData), shared_from_this()); - mImpl->devices.push_back(newDevice); - for (auto delegate : mImpl->delegates) { - delegate->bleDatabaseDidCreate(newDevice); - } - return newDevice; -} - - -template -std::shared_ptr -ConcreteBLEDatabase::device(const BLEMacAddress& mac, const Data& advert/*, const RSSI& rssi*/) -{ - // Check by MAC first - TargetIdentifier targetIdentifier((Data)mac); - auto results = matches([&targetIdentifier](const std::shared_ptr& d) { - return d->identifier() == targetIdentifier; - }); - if (results.size() != 0) { - // HDBG("DEVICE ALREADY KNOWN BY MAC"); - // Assume advert details are known already - return results.front(); // TODO ensure we send back the latest, not just the first match - // res->rssi(rssi); - // return res; - } - - // Now check by pseudo mac - auto segments = BLEAdvertParser::extractSegments(advert,0); - // HTDBG("segments:-"); - // HTDBG(std::to_string(segments.size())); - auto manuData = BLEAdvertParser::extractManufacturerData(segments); - auto heraldDataSegments = BLEAdvertParser::extractHeraldManufacturerData(manuData); - // HTDBG("herald data segments:-"); - // HTDBG(std::to_string(heraldDataSegments.size())); - // auto device = mImpl->db->device(bleMacAddress); // For most devices this will suffice - - // TODO Check for public herald service in ADV_IND packet - shown if an Android device, wearable or beacon in zephyr - // auto serviceData128 = BLEAdvertParser::extractServiceUUID128Data(segments); - // bool hasHeraldService = false; - // for (auto& service : serviceData128) { - // if (service.uuid == heraldUuidData) { - // hasHeraldService = true; - // HTDBG("FOUND DEVICE ADVERTISING HERALD SERVICE"); - // device->operatingSystem(BLEDeviceOperatingSystem::android); - // } - // } - - - if (0 != heraldDataSegments.size()) { - // HDBG("Found Herald Android pseudo device address in advert"); - // Try to FIND by pseudo first - BLEMacAddress pseudo(heraldDataSegments.front()); - auto samePseudo = matches([&pseudo](const std::shared_ptr& d) { - return d->pseudoDeviceAddress() == pseudo; - }); - if (0 != samePseudo.size()) { - // HDBG("FOUND EXISTING DEVICE BY PSEUDO"); - return samePseudo.front(); - } - // HDBG("CREATING NEW DEVICE BY MAC AND PSEUDO ONLY"); - // Now create new device with mac and pseudo - auto newDevice = device(mac,pseudo); - mImpl->assignAdvertData(newDevice,std::move(segments), manuData); - // newDevice->rssi(rssi); - return newDevice; - } - - // HDBG("CREATING NEW DEVICE BY MAC ONLY"); - - // Now create a device just from a mac - auto newDevice = device(targetIdentifier); - // HDBG("Got new device"); - mImpl->assignAdvertData(newDevice,std::move(segments), manuData); - // newDevice->rssi(rssi); - // HDBG("Assigned advert data"); - return newDevice; -} - -template -std::shared_ptr -ConcreteBLEDatabase::device(const BLEMacAddress& mac, const BLEMacAddress& pseudo) -{ - auto samePseudo = matches([&pseudo](const std::shared_ptr& d) { - return d->pseudoDeviceAddress() == pseudo; - }); - if (0 == samePseudo.size()) { - auto ptr = device(TargetIdentifier((Data)pseudo)); - ptr->pseudoDeviceAddress(pseudo); - return ptr; - } - // get most recent and clone, then attach - auto comp = last_updated_descending(); - std::sort(samePseudo.begin(),samePseudo.end(), comp); // functional style - auto newDevice = std::make_shared(*samePseudo.front()); // copy ctor used - // TODO support calling card - // auto toShare = shareDataAcrossDevices(pseudo); - // if (toShare.has_value()) { - // newDevice.payloadData(toShare); - // } - - // Has pseudo address so must be android - newDevice->operatingSystem(BLEDeviceOperatingSystem::android); - - // register new device discovery date - newDevice->registerDiscovery(Date()); - - mImpl->devices.push_back(newDevice); - for (auto delegate : mImpl->delegates) { - delegate->bleDatabaseDidCreate(newDevice); - } - return newDevice; -} - -template -std::shared_ptr -ConcreteBLEDatabase::device(const BLEMacAddress& mac) -{ - return device(TargetIdentifier((Data)mac)); -} - -template -std::shared_ptr -ConcreteBLEDatabase::device(const TargetIdentifier& targetIdentifier) -{ - auto results = matches([&targetIdentifier](const std::shared_ptr& d) { - return d->identifier() == targetIdentifier; - }); - if (results.size() != 0) { - return results.front(); // TODO ensure we send back the latest, not just the first match - } - HDBG("New target identified: {}",(std::string)targetIdentifier); - std::shared_ptr newDevice = std::make_shared( - targetIdentifier, shared_from_this()); - mImpl->devices.push_back(newDevice); - for (auto delegate : mImpl->delegates) { - delegate->bleDatabaseDidCreate(newDevice); - } - return newDevice; -} - -template -std::size_t -ConcreteBLEDatabase::size() const -{ - return mImpl->devices.size(); -} - -template -std::vector> -ConcreteBLEDatabase::matches( - const std::function&)>& matcher) const -{ - std::vector> results; - // in the absence of copy_if in C++20... Just copies the pointers not the objects - for (auto& d : mImpl->devices) { - if (matcher(d)) { - results.push_back(d); - } - } - return results; -} - -/// Cannot name a function delete in C++. remove is common. -template -void -ConcreteBLEDatabase::remove(const TargetIdentifier& targetIdentifier) -{ - auto found = std::find_if(mImpl->devices.begin(),mImpl->devices.end(), - [&targetIdentifier](std::shared_ptr& d) -> bool { - return d->identifier() == targetIdentifier; - } - ); - if (found != mImpl->devices.end()) { - std::shared_ptr toRemove = *found; - mImpl->devices.erase(found); - for (auto& delegate : mImpl->delegates) { - delegate->bleDatabaseDidDelete(toRemove); - } - } -} - -// BLE Device Delegate overrides -template -void -ConcreteBLEDatabase::device(const std::shared_ptr& device, const BLEDeviceAttribute didUpdate) -{ - // TODO update any internal DB state as necessary (E.g. deletion) - for (auto& delegate : mImpl->delegates) { - delegate->bleDatabaseDidUpdate(device, didUpdate); // TODO verify this is the right onward call - } -} - } } diff --git a/herald/src/engine/coordinator.cpp b/herald/src/engine/coordinator.cpp index 0b5b146..26f2bc2 100644 --- a/herald/src/engine/coordinator.cpp +++ b/herald/src/engine/coordinator.cpp @@ -2,241 +2,13 @@ // SPDX-License-Identifier: Apache-2.0 // -#include "herald/context.h" -#include "herald/engine/activities.h" #include "herald/engine/coordinator.h" -#include "herald/data/sensor_logger.h" #include -#include -#include -#include -#include -#include -// #include -// #include namespace herald { namespace engine { -using namespace herald::datatype; - -template -class Coordinator::Impl { -public: - Impl(ContextT& ctx); - ~Impl(); - - ContextT& context; - - std::vector> providers; - std::map> featureProviders; - - bool running; - - HLOGGER(ContextT); -}; - -template -Coordinator::Impl::Impl(ContextT& ctx) - : context(ctx), - providers(), - running(false) - HLOGGERINIT(ctx,"engine","coordinator") -{ - ; -} - -template -Coordinator::Impl::~Impl() -{ - ; -} - - - - -template -Coordinator::Coordinator(ContextT& ctx) - : mImpl(std::make_unique(ctx)) -{ - ; -} - -template -Coordinator::~Coordinator() -{ - ; -} - - -/** Introspect and include in iteration planning **/ -template -void -Coordinator::add(std::shared_ptr sensor) -{ - HDBG("Adding sensor"); - auto prov = sensor->coordinationProvider(); - if (prov.has_value()) { - HDBG("Sensor has Provider implementation"); - mImpl->providers.push_back(prov.value()); - } -} - -/** Remove from iteration planning **/ -template -void -Coordinator::remove(std::shared_ptr sensor) -{ - // TODO support remove -} - -/** Prepares for iterations to be called (may pre-emptively make calls) **/ -template -void -Coordinator::start() -{ - HDBG("Start called"); - // Clear feature providers - mImpl->featureProviders.clear(); - // Fetch feature providers - for (auto prov: mImpl->providers) { - auto myFeatures = prov->connectionsProvided(); - for (auto feature : myFeatures) { - mImpl->featureProviders.emplace(feature,prov); - } - } - mImpl->running = true; - HDBG("Start returning"); -} - -/** Execute an iteration of activity, according to settings **/ -template -void -Coordinator::iteration() -{ - if (!mImpl->running) { - HDBG("Coordinator not running. Returning from iteration having done nothing."); - return; - } - HDBG("################# ITERATION #################"); - // HDBG("Entered iteration"); - // Create empty list of required prereqs per provider - std::map,std::vector> assignPrereqs; - for (auto& prov : mImpl->providers) { - assignPrereqs.emplace(prov,std::vector()); - } - // HDBG("Completed initialisation of provider prerequisities containers"); - // HDBG(" - Provider count: {}", mImpl->providers.size()); - - std::vector connsRequired; - // Loop over providers and ask for feature pre-requisites - for (auto& prov : mImpl->providers) { - auto myConns = prov->requiredConnections(); - std::copy(myConns.begin(),myConns.end(), - std::back_insert_iterator>(connsRequired)); - } - // HDBG(std::to_string(connsRequired.size())); - // HDBG("Retrieved providers' current prerequisites"); - // TODO de-duplicate pre-reqs - // Now link required prereqs to each provider - for (auto& p : connsRequired) { - auto el = mImpl->featureProviders.find(std::get<0>(p)); // find provider for given prereq by feature tag - if (mImpl->featureProviders.end() != el) { - assignPrereqs[el->second].push_back(p); - } - } - // HDBG("Linked pre-reqs to their providers"); - - // Some debug checks here - int cnt = 0; - for (auto& ass : assignPrereqs) { - // HDBG("assign prereqs number {} has this many prereqs to fill {}", cnt, ass.second.size()); - cnt++; - } - - // Communicate with relevant feature providers and request features for targets (in descending priority order) - // - Includes removal of previous features no longer needed - std::vector provisioned; - for (auto& prov : assignPrereqs) { - // TODO sort by descending priority before passing on - - // FOR PLATFORMS WITH STD::FUTURE AND STD::ASYNC - // std::future fut = std::async(std::launch::async, - // &CoordinationProvider::provision, prov.first, - // //prov.first->provision( - // prov.second,[&provisioned] ( - // const std::vector myProvisioned) -> void { - // std::copy(myProvisioned.begin(),myProvisioned.end(), - // std::back_insert_iterator>(provisioned)); - // }); - // fut.get(); // waits for callback // TODO wait with timeout - - // FOR OTHER PLATFORMS (E.g. ZEPHYR):- - std::vector myProvisioned = prov.first->provision(prov.second); - std::copy(myProvisioned.begin(),myProvisioned.end(), - std::back_insert_iterator>(provisioned)); - } - // HDBG("All pre-requisities requests sent and responses received"); - // TODO do the above asynchronously and await callback or timeout for all - - // For each which are now present, ask for activities (in descending priority order) - for (auto& prov : mImpl->providers) { - auto maxActs = prov->requiredActivities(); - // TODO sort by descending priority before actioning - for (auto& act : maxActs) { - std::string san("Activity "); - san += act.name; - HDBG(san); - // HDBG("Checking next desired activity for prereqs being satisfied"); - // Filter requested by provisioned - bool allFound = true; - for (auto& pre : act.prerequisites) { - bool myFound = false; - for (auto& exists : provisioned) { - if (std::get<0>(pre) == std::get<0>(exists) && - std::get<1>(pre) == std::get<2>(exists)) { - myFound = true; - } - } - allFound = allFound & myFound; - if (myFound) { - HDBG(" - Prereq satisfied"); - } else { - HDBG(" - Prereq NOT SATISFIED"); - } - } - // Carry out activities with completion callbacks passed in - if (allFound) { - HDBG("All satisfied, calling activity"); - // do activity - - // FOR PLATFORMS WITH STD::ASYNC - // act.executor(act,[this] (Activity act, std::optional followOn) -> void { - // // TODO handle result - // // TODO Carry out any follow up activities - // HDBG("Activity completion callback called"); - // }); - - // FOR PLATFORMS WITHOUT - std::optional followOn = act.executor(act); - // TODO carry out follow on activity until no more follow ons (or max follow on number hit) - } - } - } - // HDBG("Leaving iteration"); - HDBG("################# END #################"); -} - -/** Closes out any existing connections/activities **/ -template -void -Coordinator::stop() -{ - mImpl->running = false; - // No-op - done in other methods (for now) -} - bool operator<(const std::shared_ptr& first, const std::shared_ptr& second) { return &(*first) < &(*second); // simple memory address comparator of item pointed TO diff --git a/herald/src/payload/simple/simple_payload_data_supplier.cpp b/herald/src/payload/simple/simple_payload_data_supplier.cpp index ffc1c88..c806e7c 100644 --- a/herald/src/payload/simple/simple_payload_data_supplier.cpp +++ b/herald/src/payload/simple/simple_payload_data_supplier.cpp @@ -18,175 +18,6 @@ namespace herald { namespace payload { namespace simple { -// using namespace herald::payload::extended; - -// class ConcreteSimplePayloadDataSupplierV1::Impl { -// public: -// Impl(Context& context, std::uint16_t countryCode, std::uint16_t stateCode, SecretKey sk, K k); -// Impl(Context& context, std::uint16_t countryCode, std::uint16_t stateCode, SecretKey sk, K k, ConcreteExtendedDataV1 ext); -// ~Impl(); -// }; - - -// ConcreteSimplePayloadDataSupplierV1::Impl::Impl(Context& context, std::uint16_t countryCode, std::uint16_t stateCode, -// SecretKey sk, K k) -// : ctx(context), country(countryCode), state(stateCode), secretKey(sk), k(k), commonPayloadHeader(), extended(), day(-1), contactIdentifiers() -// HLOGGERINIT(ctx, "Sensor", "ConcreteSimplePayloadDataSupplierV1") -// { - -// // // add length (no extended data) -// // payload.append(std::uint16_t(2)); - -// // // TODO generate data from secret key -// // payload.append(std::uint64_t(0)); -// // payload.append(std::uint64_t(0)); -// } - - -// ConcreteSimplePayloadDataSupplierV1::Impl::Impl(Context& context, std::uint16_t countryCode, std::uint16_t stateCode, -// SecretKey sk, K k, ConcreteExtendedDataV1 ext) -// : ctx(context), country(countryCode), state(stateCode), secretKey(sk), k(k), commonPayloadHeader(), extended(ext), day(-1), contactIdentifiers() -// HLOGGERINIT(ctx, "Sensor", "ConcreteSimplePayloadDataSupplierV1") -// { - -// // matchingKeys = k.matchingKeys(sk); - -// // // add length (with extended data) -// // if (extended.hasData()) { -// // payload.append(std::uint16_t(2 + extended.payload().value().size())); -// // } else { -// // payload.append(std::uint16_t(2)); -// // } - -// // // TODO generate data from secret key -// // payload.append(std::uint64_t(0)); -// // payload.append(std::uint64_t(0)); - -// // auto extPayload = extended.payload(); -// // if (extPayload.has_value()) { -// // payload.append(extPayload.value()); -// // } -// } - -// ConcreteSimplePayloadDataSupplierV1::Impl::~Impl() -// { -// ; -// } - - - - - - -// template -// ConcreteSimplePayloadDataSupplierV1::ConcreteSimplePayloadDataSupplierV1( -// ContextT& context, std::uint16_t countryCode, std::uint16_t stateCode, -// SecretKey sk, K k) -// : SimplePayloadDataSupplier(), -// ctx(context), country(countryCode), state(stateCode), secretKey(sk), k(k), -// commonPayloadHeader(), extended(), day(-1), contactIdentifiers() -// HLOGGERINIT(ctx, "Sensor", "ConcreteSimplePayloadDataSupplierV1") -// { -// commonPayloadHeader.append(std::uint8_t(0x10)); // Simple payload V1 -// commonPayloadHeader.append(country); -// commonPayloadHeader.append(state); - -// HTDBG("About to call matching keys"); -// // matchingKeys = k.matchingKeys(sk); -// HTDBG("Completed matching keys call"); -// } - -// template -// ConcreteSimplePayloadDataSupplierV1::ConcreteSimplePayloadDataSupplierV1( -// ContextT& context, std::uint16_t countryCode, std::uint16_t stateCode, -// SecretKey sk, K k, ConcreteExtendedDataV1 ext) -// : SimplePayloadDataSupplier(), -// ctx(context), country(countryCode), state(stateCode), secretKey(sk), k(k), -// commonPayloadHeader(), extended(ext), day(-1), contactIdentifiers() -// HLOGGERINIT(ctx, "Sensor", "ConcreteSimplePayloadDataSupplierV1") -// { -// commonPayloadHeader.append(std::uint8_t(0x10)); // Simple payload V1 -// commonPayloadHeader.append(country); -// commonPayloadHeader.append(state); -// } - -// template -// ConcreteSimplePayloadDataSupplierV1::~ConcreteSimplePayloadDataSupplierV1() -// { -// ; -// } - -// template -// std::optional -// ConcreteSimplePayloadDataSupplierV1::legacyPayload(const PayloadTimestamp timestamp, const std::shared_ptr device) -// { -// return {}; -// } - -// template -// std::optional -// ConcreteSimplePayloadDataSupplierV1::payload(const PayloadTimestamp timestamp, const std::shared_ptr device) -// { -// const int day = mImpl->k.day(timestamp.value); -// const int period = mImpl->k.period(timestamp.value); - -// // if (!(day >= 0 && day < mImpl->matchingKeys.size())) { -// // HERR("Contact identifier out of day range"); -// // return {}; -// // } - -// auto cid = mImpl->k.contactIdentifier(mImpl->secretKey,day,period); - -// // if (-1 == mImpl->day || day != mImpl->day) { -// // // generate new matching keys -// // mImpl->day = day; -// // auto contactKeys = mImpl->k.contactKeys(mImpl->matchingKeys[day]); -// // mImpl->contactIdentifiers.clear(); -// // mImpl->contactIdentifiers.reserve(contactKeys.size()); -// // for (int i = 0;i < contactKeys.size();i++) { -// // mImpl->contactIdentifiers.emplace_back(); -// // } -// // for (int i = contactKeys.size() - 1;i >= 0;i--) { -// // mImpl->contactIdentifiers[i].append(mImpl->k.contactIdentifier(contactKeys[i])); -// // } -// // } - -// // contact identifiers is always populated, so no error condition check here - -// // if (!(period >=0 && period < mImpl->contactIdentifiers.size())) { -// // HERR("Contact identifier out of period range"); -// // return {}; -// // } - -// // Defensive check -// // if (mImpl->contactIdentifiers[period].size() != 16) { -// // HERR("Contact identifier not 16 bytes"); -// // return {}; -// // } - -// PayloadData p(mImpl->commonPayloadHeader); -// // length -// if (mImpl->extended.hasData()) { -// p.append(std::uint16_t(2 + mImpl->extended.payload().value().size())); -// } else { -// p.append(std::uint16_t(2)); -// } -// // contact id -// p.append(cid); -// // extended data -// if (mImpl->extended.hasData()) { -// p.append(mImpl->extended.payload().value()); -// } - -// return std::optional{p}; -// } - -// template -// std::vector -// ConcreteSimplePayloadDataSupplierV1::payload(const Data& data) -// { -// return std::vector(); -// } } } From 46d8320912a124fc8e495df10957f31e97dbb637 Mon Sep 17 00:00:00 2001 From: Adam Fowler Date: Sat, 27 Mar 2021 21:34:04 +0000 Subject: [PATCH 19/28] Refactoring Zephyr implementations for templates Outstanding items:- - Move zephyr transmitter and receiver code to new zephyr tpl header files - Move concrete ble sensor/database to just ble sensor/database - Sort out warning for logging uint64_t (specific override in our logger using std::to_string? - Sort out static signal_characteristic_data methods - Compare flash and memory use to previous build - Backport FDH config items to main Signed-off-by: Adam Fowler --- herald-wearable/src/main.cpp | 126 ++--- herald/herald.cmake | 2 + herald/include/herald.h | 5 + herald/include/herald/ble/ble_concrete.h | 505 ++++++------------ .../herald/ble/ble_concrete_database.h | 330 ++++++++++++ .../herald/ble/zephyr/concrete_ble_receiver.h | 82 +++ .../ble/zephyr/concrete_ble_transmitter.h | 63 +++ herald/include/herald/data/sensor_logger.h | 4 +- .../herald/data/zephyr/zephyr_logging_sink.h | 11 +- .../simple/simple_payload_data_supplier.h | 2 +- herald/include/herald/sensor_array.h | 87 ++- herald/include/herald/zephyr_context.h | 2 + herald/src/ble/concrete_ble_sensor.cpp | 416 +++++++-------- .../src/ble/zephyr/concrete_ble_receiver.cpp | 134 +++-- .../ble/zephyr/concrete_ble_transmitter.cpp | 45 +- .../src/data/zephyr/zephyr_logging_sink.cpp | 5 +- herald/src/sensor_array.cpp | 279 +++++----- herald/src/zephyr_context.cpp | 4 +- 18 files changed, 1239 insertions(+), 863 deletions(-) create mode 100644 herald/include/herald/ble/ble_concrete_database.h create mode 100644 herald/include/herald/ble/zephyr/concrete_ble_receiver.h create mode 100644 herald/include/herald/ble/zephyr/concrete_ble_transmitter.h diff --git a/herald-wearable/src/main.cpp b/herald-wearable/src/main.cpp index 7d236fc..8a7fd6d 100644 --- a/herald-wearable/src/main.cpp +++ b/herald-wearable/src/main.cpp @@ -40,7 +40,12 @@ #include #include -LOG_MODULE_REGISTER(app, CONFIG_APP_LOG_LEVEL); +namespace applogging { + LOG_MODULE_REGISTER(app, CONFIG_APP_LOG_LEVEL); + #define APP_DBG(_msg,...) LOG_DBG(_msg,##__VA_ARGS__); + #define APP_INF(_msg,...) LOG_INF(_msg,##__VA_ARGS__); + #define APP_ERR(_msg,...) LOG_ERR(_msg,##__VA_ARGS__); +} /* 1000 msec = 1 sec */ #define SLEEP_TIME_MS 1000 @@ -68,8 +73,6 @@ using namespace herald::data; using namespace herald::payload; using namespace herald::payload::fixed; -std::shared_ptr sa; - char* str(const TargetIdentifier& ti) { return log_strdup( ((std::string)ti).c_str()); } @@ -81,24 +84,24 @@ class AppLoggingDelegate : public herald::SensorDelegate { void sensor(SensorType sensor, const TargetIdentifier& didDetect) override { // LOG_DBG("sensor didDetect"); - LOG_DBG("sensor didDetect: %s", str(didDetect) ); // May want to disable this - logs A LOT of info + APP_DBG("sensor didDetect: %s", str(didDetect) ); // May want to disable this - logs A LOT of info } /// Read payload data from target, e.g. encrypted device identifier from BLE peripheral after successful connection. void sensor(SensorType sensor, const PayloadData& didRead, const TargetIdentifier& fromTarget) override { // LOG_DBG("sensor didRead"); - LOG_DBG("sensor didRead: %s with payload: %s", str(fromTarget), log_strdup(didRead.hexEncodedString().c_str())); + APP_DBG("sensor didRead: %s with payload: %s", str(fromTarget), log_strdup(didRead.hexEncodedString().c_str())); } /// Receive written immediate send data from target, e.g. important timing signal. void sensor(SensorType sensor, const ImmediateSendData& didReceive, const TargetIdentifier& fromTarget) override { // LOG_DBG("sensor didReceive"); - LOG_DBG("sensor didReceive: %s with immediate send data: %s", str(fromTarget), log_strdup(didReceive.hexEncodedString().c_str())); + APP_DBG("sensor didReceive: %s with immediate send data: %s", str(fromTarget), log_strdup(didReceive.hexEncodedString().c_str())); } /// Read payload data of other targets recently acquired by a target, e.g. Android peripheral sharing payload data acquired from nearby iOS peripherals. void sensor(SensorType sensor, const std::vector& didShare, const TargetIdentifier& fromTarget) override { - LOG_DBG("sensor didShare"); + APP_DBG("sensor didShare"); // LOG_DBG("sensor didShare: %s", str(fromTarget) ); // for (auto& p : didShare) { // LOG_DBG(" - %s", log_strdup(p.hexEncodedString().c_str())); @@ -107,24 +110,24 @@ class AppLoggingDelegate : public herald::SensorDelegate { /// Measure proximity to target, e.g. a sample of RSSI values from BLE peripheral. void sensor(SensorType sensor, const Proximity& didMeasure, const TargetIdentifier& fromTarget) override { - LOG_DBG("sensor didMeasure"); + APP_DBG("sensor didMeasure"); // LOG_DBG("sensor didMeasure: %s with proximity: %d", str(fromTarget), didMeasure.value); } /// Detection of time spent at location, e.g. at specific restaurant between 02/06/2020 19:00 and 02/06/2020 21:00 template void sensor(SensorType sensor, const Location& didVisit) { - LOG_DBG("sensor didVisit"); + APP_DBG("sensor didVisit"); } /// Measure proximity to target with payload data. Combines didMeasure and didRead into a single convenient delegate method void sensor(SensorType sensor, const Proximity& didMeasure, const TargetIdentifier& fromTarget, const PayloadData& withPayload) override { - LOG_DBG("sensor didMeasure withPayload"); + APP_DBG("sensor didMeasure withPayload"); } /// Sensor state update void sensor(SensorType sensor, const SensorState& didUpdateState) override { - LOG_DBG("sensor didUpdateState"); + APP_DBG("sensor didUpdateState"); } }; @@ -165,22 +168,24 @@ void cc3xx_init() { #endif void herald_entry() { - LOG_DBG("Herald entry"); + APP_DBG("Herald entry"); k_sleep(K_MSEC(10000)); // pause so we have time to see Herald initialisation log messages. Don't do this in production! - LOG_DBG("Herald setup begins"); + APP_DBG("Herald setup begins"); // Test date/time based things on Zephyr - interesting issues with compliance! Date now; - LOG_DBG("BEFORE DATE"); + APP_DBG("BEFORE DATE"); std::string s = now.iso8601DateTime(); - LOG_DBG("DATE: %s", log_strdup(s.c_str())); - LOG_DBG("PAST DATE"); + APP_DBG("DATE: %s", log_strdup(s.c_str())); + APP_DBG("PAST DATE"); std::shared_ptr appDelegate = std::make_shared(); // IMPLEMENTORS GUIDANCE - USING HERALD // First initialise the Zephyr Context - this links Herald to any Zephyr OS specific constructs or callbacks - std::shared_ptr ctx = std::make_shared(); + herald::ZephyrContextProvider zcp; + herald::Context ctx(zcp.getLoggingSink(),zcp.getBluetoothStateManager()); + using CT = Context; // Now prepare your device's Herald identity payload - this is what gets sent to other devices when they request it // SECURITY: Depending on the payload provider, this could be static and in the clear or varying over time. @@ -196,12 +201,12 @@ void herald_entry() { // 7. Implement a consistent post restart valid ID from a hardware identifier (E.g. nRF serial number) auto hwInfoAvailable = hwinfo_get_device_id(uniqueId,sizeof(uniqueId)); if (hwInfoAvailable > 0) { - LOG_DBG("Read %d bytes for a unique, persistent, device ID", hwInfoAvailable); + APP_DBG("Read %d bytes for a unique, persistent, device ID", hwInfoAvailable); clientId = *uniqueId; } else { - LOG_DBG("Couldn't read hardware info for zephyr device. Error code: %d", hwInfoAvailable); + APP_DBG("Couldn't read hardware info for zephyr device. Error code: %d", hwInfoAvailable); } - LOG_DBG("Final clientID: %d", clientId); + APP_DBG("Final clientID: %d", clientId); std::shared_ptr pds = std::make_shared( countryCode, @@ -211,7 +216,7 @@ void herald_entry() { // END TESTING ONLY // PRODUCTION ONLY -// LOG_DBG("Before simple"); +// APP_DBG("Before simple"); // k_sleep(K_SECONDS(2)); // // Use the simple payload, or secured payload, that implements privacy features to prevent user tracking // herald::payload::simple::K k; @@ -232,18 +237,18 @@ void herald_entry() { // if (0 == success) { // sk.clear(); // sk.append(buf, 0, buflen); -// LOG_DBG("Have applied CC3xx generated data to secret key"); +// APP_DBG("Have applied CC3xx generated data to secret key"); // } else { -// LOG_DBG("Could not generate 2048 bytes of randomness required for SimplePayload Secret Key. Falling back to fixed generic secret key."); +// APP_DBG("Could not generate 2048 bytes of randomness required for SimplePayload Secret Key. Falling back to fixed generic secret key."); // } // // verify secret key // for (int i = 0;i < 2048;i+=64) { // Data t = sk.subdata(i,64); -// LOG_DBG("Got 64 bytes from secret key from %d",i); +// APP_DBG("Got 64 bytes from secret key from %d",i); // } -// LOG_DBG("About to create Payload data supplier"); +// APP_DBG("About to create Payload data supplier"); // k_sleep(K_SECONDS(2)); // std::shared_ptr pds = std::make_shared( @@ -254,36 +259,37 @@ void herald_entry() { // k // ); // END PRODUCTION ONLY - LOG_DBG("Have created Payload data supplier"); + APP_DBG("Have created Payload data supplier"); k_sleep(K_SECONDS(2)); - auto sink = ctx->getLoggingSink("mySub","myCat"); - sink->log(SensorLoggerLevel::debug,"Here's some info for you"); + auto sink = ctx.getLoggingSink(); + sink.log("subsys1","cat1",SensorLoggerLevel::debug,"Here's some info for you"); auto payload = pds->payload(PayloadTimestamp(),nullptr); - sink->log(SensorLoggerLevel::debug,"I've got some payload data"); - sink->log(SensorLoggerLevel::debug,payload->hexEncodedString()); + sink.log("subsys1","cat1",SensorLoggerLevel::debug,"I've got some payload data"); + sink.log("subsys1","cat1",SensorLoggerLevel::debug,payload->hexEncodedString()); - auto sink2 = ctx->getLoggingSink("mySub","mySecondCat"); - sink2->log(SensorLoggerLevel::debug,"Here's some more info for you"); + auto sink2 = ctx.getLoggingSink(); + sink2.log("subsys2","cat2",SensorLoggerLevel::debug,"Here's some more info for you"); // LOGGING LEVEL TESTING - LOG_DBG("Zephyr debug message"); - LOG_INF("Zephyr info message"); - LOG_ERR("Zephyr error message"); - sink2->log(SensorLoggerLevel::debug,"Herald debug message"); - sink2->log(SensorLoggerLevel::info,"Herald info message"); - sink2->log(SensorLoggerLevel::fault,"Herald error message"); + APP_DBG("Zephyr debug message"); + APP_INF("Zephyr info message"); + APP_ERR("Zephyr error message"); + sink2.log("subsys2","cat2",SensorLoggerLevel::debug,"Herald debug message"); + sink2.log("subsys2","cat2",SensorLoggerLevel::info,"Herald info message"); + sink2.log("subsys2","cat2",SensorLoggerLevel::fault,"Herald error message"); // Enable transmitter (i.e. this is a Herald device) BLESensorConfiguration::advertisingEnabled = true; - LOG_DBG("Creating sensor array"); + APP_DBG("Creating sensor array"); k_sleep(K_SECONDS(2)); // Create Herald sensor array - this handles both advertising (Transmitter) and scanning/connecting (Receiver) - sa = std::make_shared(ctx,pds); + SensorArray sa(ctx,pds); + ConcreteBLESensor,ConcreteBLEReceiver> ble(ctx); // Add contacts.log delegate // CURRENTLY BREAKS ZEPHYR - DON'T KNOW WHY YET - LOGGING SUBSYSTEM ISSUE @@ -292,34 +298,34 @@ void herald_entry() { // sa->add(contactLogger); // Note: You will likely want to register a SensorDelegate implementation of your own to the sensor array to get callbacks on nearby devices - sa->add(appDelegate); + sa.add(appDelegate); // 3. Create and add a Logging sensor delegate to enable testing of discovery // 4. Now create a live analysis pipeline and enable RSSI to be sent to it for distance estimation - herald::analysis::algorithms::distance::FowlerBasicAnalyser distanceAnalyser(0, -50, -24); // 0 = run every time run() is called + // herald::analysis::algorithms::distance::FowlerBasicAnalyser distanceAnalyser(0, -50, -24); // 0 = run every time run() is called - herald::analysis::LoggingAnalysisDelegate myDelegate(ctx); - herald::analysis::AnalysisDelegateManager adm(std::move(myDelegate)); // NOTE: myDelegate MOVED FROM and no longer accessible - herald::analysis::AnalysisProviderManager apm(std::move(distanceAnalyser)); // NOTE: distanceAnalyser MOVED FROM and no longer accessible + // herald::analysis::LoggingAnalysisDelegate myDelegate(ctx); + // herald::analysis::AnalysisDelegateManager adm(std::move(myDelegate)); // NOTE: myDelegate MOVED FROM and no longer accessible + // herald::analysis::AnalysisProviderManager apm(std::move(distanceAnalyser)); // NOTE: distanceAnalyser MOVED FROM and no longer accessible - herald::analysis::AnalysisRunner< - herald::analysis::AnalysisDelegateManager>, - herald::analysis::AnalysisProviderManager, - RSSI,Distance - > runner(adm, apm); // just for Sample types, and their produced output (Sample) + // herald::analysis::AnalysisRunner< + // herald::analysis::AnalysisDelegateManager>, + // herald::analysis::AnalysisProviderManager, + // RSSI,Distance + // > runner(adm, apm); // just for Sample types, and their produced output (Sample) - std::shared_ptr> src = std::make_shared>(runner); - sa->add(src); + // std::shared_ptr> src = std::make_shared>(runner); + // sa->add(src); - LOG_DBG("Starting sensor array"); + APP_DBG("Starting sensor array"); k_sleep(K_SECONDS(2)); // Start array (and thus start advertising) - sa->start(); // There's a corresponding stop() call too + sa.start(); // There's a corresponding stop() call too int iter = 0; Date last; @@ -329,11 +335,11 @@ void herald_entry() { Date now; if (iter > 40 /* && iter < 44 */ ) { // some delay to allow us to see advertising output // You could only do first 3 iterations so we can see the older log messages without continually scrolling through log messages - sa->iteration(now - last); + sa.iteration(now - last); } if (0 == iter % (5000 / delay)) { - LOG_DBG("herald thread still running. Iteration: %d", iter); + APP_DBG("herald thread still running. Iteration: %d", iter); // runner.run(Date()); // Note: You may want to do this less or more regularly depending on your requirements } @@ -358,9 +364,9 @@ void main(void) return; } - LOG_DBG("Logging test"); - LOG_DBG("Const char* param test: %s","some string param"); - LOG_DBG("int param test: %d",1234); + APP_DBG("Logging test"); + APP_DBG("Const char* param test: %s","some string param"); + APP_DBG("int param test: %d",1234); #ifdef CC3XX_BACKEND cc3xx_init(); @@ -383,7 +389,7 @@ void main(void) gpio_pin_set(dev, PIN, (int)led_is_on); led_is_on = !led_is_on; - LOG_DBG("main thread still running"); + APP_DBG("main thread still running"); // TODO Add logic here to detect failure in Herald thread, and restart to resume as necessary } diff --git a/herald/herald.cmake b/herald/herald.cmake index 63a647e..d5584c9 100644 --- a/herald/herald.cmake +++ b/herald/herald.cmake @@ -79,6 +79,8 @@ set(HERALD_HEADERS ) set(HERALD_HEADERS_ZEPHYR + ${HERALD_BASE}/include/herald/ble/zephyr/concrete_ble_receiver.h + ${HERALD_BASE}/include/herald/ble/zephyr/concrete_ble_transmitter.h ${HERALD_BASE}/include/herald/data/zephyr/zephyr_logging_sink.h ${HERALD_BASE}/include/herald/zephyr_context.h ) diff --git a/herald/include/herald.h b/herald/include/herald.h index c496851..67835d0 100644 --- a/herald/include/herald.h +++ b/herald/include/herald.h @@ -21,6 +21,9 @@ #ifdef __ZEPHYR__ #include "herald/zephyr_context.h" +#include "herald/data/zephyr/zephyr_logging_sink.h" +#include "herald/ble/zephyr/concrete_ble_transmitter.h" +#include "herald/ble/zephyr/concrete_ble_receiver.h" #endif // Datatype namespace @@ -53,6 +56,7 @@ #include "herald/data/contact_log.h" #include "herald/data/payload_data_formatter.h" #include "herald/data/sensor_logger.h" +#include "herald/data/stdout_logging_sink.h" // engine namespace #include "herald/engine/activities.h" @@ -78,6 +82,7 @@ #include "herald/ble/filter/ble_advert_parser.h" #include "herald/ble/ble_concrete.h" +#include "herald/ble/ble_concrete_database.h" // analysis namespace #include "herald/analysis/aggregates.h" diff --git a/herald/include/herald/ble/ble_concrete.h b/herald/include/herald/ble/ble_concrete.h index a60f6a9..7382f96 100644 --- a/herald/include/herald/ble/ble_concrete.h +++ b/herald/include/herald/ble/ble_concrete.h @@ -5,10 +5,12 @@ #ifndef BLE_CONCRETE_H #define BLE_CONCRETE_H +#include "ble_concrete_database.h" #include "ble_database.h" #include "ble_receiver.h" #include "ble_sensor.h" #include "ble_transmitter.h" +#include "ble_concrete.h" #include "ble_protocols.h" #include "bluetooth_state_manager.h" #include "ble_device_delegate.h" @@ -16,10 +18,14 @@ #include "../payload/payload_data_supplier.h" #include "../context.h" #include "../data/sensor_logger.h" +#include "ble_sensor_configuration.h" +#include "ble_coordinator.h" +#include "../datatype/bluetooth_state.h" #include #include #include +#include namespace herald { namespace ble { @@ -32,408 +38,195 @@ using namespace herald::payload; // SPECIFIC PLATFORM DEFINITIONS ARE WITHIN SEVERAL C++ FILES // UNDER WINDOWS AND ZEPHYR SUB DIRECTORIES -class ConcreteBluetoothStateManager : public BluetoothStateManager, public std::enable_shared_from_this { -public: - ConcreteBluetoothStateManager(); - ConcreteBluetoothStateManager(const ConcreteBluetoothStateManager& from) = delete; - ConcreteBluetoothStateManager(ConcreteBluetoothStateManager&& from) = delete; - ~ConcreteBluetoothStateManager(); +// class ConcreteBluetoothStateManager : public BluetoothStateManager, public std::enable_shared_from_this { +// public: +// ConcreteBluetoothStateManager(); +// ConcreteBluetoothStateManager(const ConcreteBluetoothStateManager& from) = delete; +// ConcreteBluetoothStateManager(ConcreteBluetoothStateManager&& from) = delete; +// ~ConcreteBluetoothStateManager(); + +// // Bluetooth State Manager overrides +// void add(std::shared_ptr delegate) override; +// BluetoothState state() override; +// }; - // Bluetooth State Manager overrides - void add(std::shared_ptr delegate) override; - BluetoothState state() override; -}; -/// \brief Provides a callable that assists in ordering for most recently updated BLEDevice -struct last_updated_descending { - bool operator()(const std::shared_ptr& a, const std::shared_ptr& b) { - return a->timeIntervalSinceLastUpdate() > b->timeIntervalSinceLastUpdate(); // opposite order - } -}; -template -class ConcreteBLEDatabase : public BLEDatabase, public BLEDeviceDelegate, public std::enable_shared_from_this> { +/** + * Acts as the main object to control the receiver, transmitter, and database instances + */ +template +class ConcreteBLESensor : public BLESensor, public BLEDatabaseDelegate, + public BluetoothStateManagerDelegate, public std::enable_shared_from_this> { public: - ConcreteBLEDatabase(ContextT& context) - : ctx(context), + ConcreteBLESensor(ContextT& ctx, BluetoothStateManager& bluetoothStateManager, + std::shared_ptr payloadDataSupplier) + : database(ctx), + stateManager(bluetoothStateManager), + transmitter(ctx, bluetoothStateManager, payloadDataSupplier, database), + receiver(ctx, bluetoothStateManager, payloadDataSupplier, database), delegates(), - devices() - HLOGGERINIT(context,"herald","ConcreteBLEDatabase") + coordinator(std::make_shared>(ctx, database, receiver)), + addedSelfAsDelegate(false) + HLOGGERINIT(ctx,"sensor","ConcreteBLESensor") { - ; - } - - ConcreteBLEDatabase(const ConcreteBLEDatabase& from) = delete; - ConcreteBLEDatabase(ConcreteBLEDatabase&& from) = delete; - - ~ConcreteBLEDatabase() = default; - - // BLE Database overrides - - void add(const std::shared_ptr& delegate) override { - delegates.push_back(delegate); } - // Creation overrides - std::shared_ptr device(const BLEMacAddress& mac, const Data& advert/*, const RSSI& rssi*/) override { - // Check by MAC first - TargetIdentifier targetIdentifier((Data)mac); - auto results = matches([&targetIdentifier](const std::shared_ptr& d) { - return d->identifier() == targetIdentifier; - }); - if (results.size() != 0) { - // HDBG("DEVICE ALREADY KNOWN BY MAC"); - // Assume advert details are known already - return results.front(); // TODO ensure we send back the latest, not just the first match - // res->rssi(rssi); - // return res; - } - - // Now check by pseudo mac - auto segments = BLEAdvertParser::extractSegments(advert,0); - // HTDBG("segments:-"); - // HTDBG(std::to_string(segments.size())); - auto manuData = BLEAdvertParser::extractManufacturerData(segments); - auto heraldDataSegments = BLEAdvertParser::extractHeraldManufacturerData(manuData); - // HTDBG("herald data segments:-"); - // HTDBG(std::to_string(heraldDataSegments.size())); - // auto device = db->device(bleMacAddress); // For most devices this will suffice - - // TODO Check for public herald service in ADV_IND packet - shown if an Android device, wearable or beacon in zephyr - // auto serviceData128 = BLEAdvertParser::extractServiceUUID128Data(segments); - // bool hasHeraldService = false; - // for (auto& service : serviceData128) { - // if (service.uuid == heraldUuidData) { - // hasHeraldService = true; - // HTDBG("FOUND DEVICE ADVERTISING HERALD SERVICE"); - // device->operatingSystem(BLEDeviceOperatingSystem::android); - // } - // } - + ConcreteBLESensor(const ConcreteBLESensor& from) = delete; + ConcreteBLESensor(ConcreteBLESensor&& from) = delete; + ~ConcreteBLESensor() = default; - if (0 != heraldDataSegments.size()) { - // HDBG("Found Herald Android pseudo device address in advert"); - // Try to FIND by pseudo first - BLEMacAddress pseudo(heraldDataSegments.front()); - auto samePseudo = matches([&pseudo](const std::shared_ptr& d) { - return d->pseudoDeviceAddress() == pseudo; - }); - if (0 != samePseudo.size()) { - // HDBG("FOUND EXISTING DEVICE BY PSEUDO"); - return samePseudo.front(); - } - // HDBG("CREATING NEW DEVICE BY MAC AND PSEUDO ONLY"); - // Now create new device with mac and pseudo - auto newDevice = device(mac,pseudo); - assignAdvertData(newDevice,std::move(segments), manuData); - // newDevice->rssi(rssi); - return newDevice; + // Coordination overrides - Since v1.2-beta3 + std::optional> coordinationProvider() override { + // Only return this if we support scanning + if (BLESensorConfiguration::scanningEnabled) { + HTDBG("Providing a BLECoordinationProvider"); + return std::optional>(coordinator); } - - // HDBG("CREATING NEW DEVICE BY MAC ONLY"); - - // Now create a device just from a mac - auto newDevice = device(targetIdentifier); - // HDBG("Got new device"); - assignAdvertData(newDevice,std::move(segments), manuData); - // newDevice->rssi(rssi); - // HDBG("Assigned advert data"); - return newDevice; + HTDBG("Scanning not supported - so not returning a BLECoordinationProvider"); + return {}; } - std::shared_ptr device(const BLEMacAddress& mac, const BLEMacAddress& pseudo) override { - auto samePseudo = matches([&pseudo](const std::shared_ptr& d) { - return d->pseudoDeviceAddress() == pseudo; - }); - if (0 == samePseudo.size()) { - auto ptr = device(TargetIdentifier((Data)pseudo)); - ptr->pseudoDeviceAddress(pseudo); - return ptr; - } - // get most recent and clone, then attach - auto comp = last_updated_descending(); - std::sort(samePseudo.begin(),samePseudo.end(), comp); // functional style - auto newDevice = std::make_shared(*samePseudo.front()); // copy ctor used - // TODO support calling card - // auto toShare = shareDataAcrossDevices(pseudo); - // if (toShare.has_value()) { - // newDevice.payloadData(toShare); - // } - - // Has pseudo address so must be android - newDevice->operatingSystem(BLEDeviceOperatingSystem::android); - - // register new device discovery date - newDevice->registerDiscovery(Date()); - - devices.push_back(newDevice); - for (auto delegate : delegates) { - delegate->bleDatabaseDidCreate(newDevice); - } - return newDevice; + bool immediateSend(Data data, const TargetIdentifier& targetIdentifier) { + return receiver.immediateSend(data,targetIdentifier); } - std::shared_ptr device(const BLEMacAddress& mac) override { - return device(TargetIdentifier((Data)mac)); + bool immediateSendAll(Data data) { + return receiver.immediateSendAll(data); } - std::shared_ptr device(const PayloadData& payloadData) override { - auto results = matches([&payloadData](const std::shared_ptr& d) { - auto payload = d->payloadData(); - if (!payload.has_value()) { - return false; - } - return (*payload)==payloadData; - }); - if (results.size() != 0) { - return results.front(); // TODO ensure we send back the latest, not just the first match - } - std::shared_ptr newDevice = std::make_shared( - TargetIdentifier(payloadData), this->shared_from_this()); - devices.push_back(newDevice); - for (auto delegate : delegates) { - delegate->bleDatabaseDidCreate(newDevice); - } - return newDevice; - } - std::shared_ptr device(const TargetIdentifier& targetIdentifier) override { - auto results = matches([&targetIdentifier](const std::shared_ptr& d) { - return d->identifier() == targetIdentifier; - }); - if (results.size() != 0) { - return results.front(); // TODO ensure we send back the latest, not just the first match - } - HTDBG("New target identified: {}",(std::string)targetIdentifier); - std::shared_ptr newDevice = std::make_shared( - targetIdentifier, this->shared_from_this()); - devices.push_back(newDevice); - for (auto delegate : delegates) { - delegate->bleDatabaseDidCreate(newDevice); - } - return newDevice; - } - - // Introspection overrides - std::size_t size() const override { - return devices.size(); + // Sensor overrides + void add(const std::shared_ptr& delegate) override { + delegates.push_back(delegate); + // add all delegates to receiver and transmitter too? + receiver.add(delegate); + transmitter.add(delegate); + // TODO what about duplicates? } - std::vector> matches( - const std::function&)>& matcher) const override { - std::vector> results; - // in the absence of copy_if in C++20... Just copies the pointers not the objects - for (auto& d : devices) { - if (matcher(d)) { - results.push_back(d); - } + void start() override { + if (!addedSelfAsDelegate) { + stateManager.add(this->shared_from_this()); // FAILS IF USED IN THE CTOR - DO NOT DO THIS FROM CTOR + database.add(this->shared_from_this()); + addedSelfAsDelegate = true; + } + transmitter.start(); + receiver.start(); + for (auto& delegate : delegates) { + delegate->sensor(SensorType::BLE, SensorState::on); } - return results; } - // std::vector> devices() const override; - - /// Cannot name a function delete in C++. remove is common. - void remove(const TargetIdentifier& targetIdentifier) override { - auto found = std::find_if(devices.begin(),devices.end(), - [&targetIdentifier](std::shared_ptr& d) -> bool { - return d->identifier() == targetIdentifier; - } - ); - if (found != devices.end()) { - std::shared_ptr toRemove = *found; - devices.erase(found); - for (auto& delegate : delegates) { - delegate->bleDatabaseDidDelete(toRemove); - } + void stop() override { + transmitter.stop(); + receiver.stop(); + for (auto& delegate : delegates) { + delegate->sensor(SensorType::BLE, SensorState::off); } } - // std::optional payloadSharingData(const std::shared_ptr& peer) override; - - // BLE Device Delegate overrides - void device(const std::shared_ptr& device, BLEDeviceAttribute didUpdate) override { - // TODO update any internal DB state as necessary (E.g. deletion) + // Database overrides + void bleDatabaseDidCreate(const std::shared_ptr& device) override { for (auto& delegate : delegates) { - delegate->bleDatabaseDidUpdate(device, didUpdate); // TODO verify this is the right onward call + delegate->sensor(SensorType::BLE, device->identifier()); // didDetect } } -private: - void assignAdvertData(std::shared_ptr& newDevice, std::vector&& toMove, - const std::vector& manuData) - { - newDevice->advertData(std::move(toMove)); - - // If it's an apple device, check to see if its on our ignore list - auto appleDataSegments = BLEAdvertParser::extractAppleManufacturerSegments(manuData); - if (0 != appleDataSegments.size()) { - HTDBG("Found apple device"); - // HTDBG((std::string)mac); - newDevice->operatingSystem(BLEDeviceOperatingSystem::ios); - // TODO see if we should ignore this Apple device - // TODO abstract these out eventually in to BLEDevice class - bool ignore = false; - /* - "^10....04", - "^10....14", - "^0100000000000000000000000000000000", - "^05","^07","^09", - "^00","^1002","^06","^08","^03","^0C","^0D","^0F","^0E","^0B" - */ - for (auto& segment : appleDataSegments) { - HTDBG(segment.data.hexEncodedString()); - switch (segment.type) { - case 0x00: - case 0x05: - case 0x07: - case 0x09: - case 0x06: - case 0x08: - case 0x03: - case 0x0C: - case 0x0D: - case 0x0F: - case 0x0E: - case 0x0B: - ignore = true; - break; - case 0x10: - // check if second is 02 - if (segment.data.at(0) == std::byte(0x02)) { - ignore = true; - } else { - // Check 3rd data bit for 14 or 04 - if (segment.data.at(2) == std::byte(0x04) || segment.data.at(2) == std::byte(0x14)) { - ignore = true; - } + void bleDatabaseDidUpdate(const std::shared_ptr& device, const BLEDeviceAttribute attribute) override { + switch (attribute) { + case BLEDeviceAttribute::rssi: { + auto rssi = device->rssi(); + if (rssi.has_value()) { + double rssiValue = (double)rssi->intValue(); + auto prox = Proximity{.unit=ProximityMeasurementUnit::RSSI, .value=rssiValue}; + for (auto& delegate: delegates) { + delegate->sensor(SensorType::BLE, + prox, + device->identifier() + ); // didMeasure + } + // also payload with rssi + auto payload = device->payloadData(); + if (payload.has_value()) { + for (auto& delegate: delegates) { + delegate->sensor(SensorType::BLE, + prox, + device->identifier(), + *payload + ); // didMeasure withPayload + } + } + } + break; + } + case BLEDeviceAttribute::payloadData: { + auto payload = device->payloadData(); + if (payload.has_value()) { + for (auto& delegate: delegates) { + delegate->sensor(SensorType::BLE, + *payload, + device->identifier() + ); // didReadPayload + } + // also payload with rssi + auto rssi = device->rssi(); + if (rssi.has_value()) { + double rssiValue = (double)rssi->intValue(); + auto prox = Proximity{.unit=ProximityMeasurementUnit::RSSI, .value=rssiValue}; + for (auto& delegate: delegates) { + delegate->sensor(SensorType::BLE, + prox, + device->identifier(), + *payload + ); // didMeasure withPayload } - break; - default: - break; + } } + break; } - if (ignore) { - HTDBG(" - Ignoring Apple device due to Apple data filter"); - newDevice->ignore(true); - } else { - // Perform GATT service discovery to check for Herald service - // NOTE: Happens from Connection request (handled by BLE Coordinator) - HTDBG(" - Unknown apple device... Logging so we can discover services later"); + default: { + ; // do nothing } - } else { - // Not a Herald android or any iOS - so inspect later (beacon or wearable) - HTDBG("Unknown non Herald device - inspecting (might be a venue beacon or wearable)"); - // HTDBG((std::string)mac); } } - ContextT& ctx; - std::vector> delegates; - std::vector> devices; - - HLOGGER(ContextT); -}; - -/** - * Acts as the main object to control the receiver, transmitter, and database instances - */ -template -class ConcreteBLESensor : public BLESensor, public BLEDatabaseDelegate, - public BluetoothStateManagerDelegate, public std::enable_shared_from_this> { -public: - ConcreteBLESensor(ContextT& ctx, BluetoothStateManager& bluetoothStateManager, - std::shared_ptr payloadDataSupplier); - ConcreteBLESensor(const ConcreteBLESensor& from) = delete; - ConcreteBLESensor(ConcreteBLESensor&& from) = delete; - ~ConcreteBLESensor(); - - // Coordination overrides - Since v1.2-beta3 - std::optional> coordinationProvider() override; - - bool immediateSend(Data data, const TargetIdentifier& targetIdentifier); - bool immediateSendAll(Data data); - - // Sensor overrides - void add(const std::shared_ptr& delegate) override; - void start() override; - void stop() override; - - // Database overrides - void bleDatabaseDidCreate(const std::shared_ptr& device) override; - void bleDatabaseDidUpdate(const std::shared_ptr& device, const BLEDeviceAttribute attribute) override; - void bleDatabaseDidDelete(const std::shared_ptr& device) override; + void bleDatabaseDidDelete(const std::shared_ptr& device) override { + ; // TODO just log this // TODO determine if to pass this on too + } // Bluetooth state manager delegate overrides - void bluetoothStateManager(BluetoothState didUpdateState) override; + void bluetoothStateManager(BluetoothState didUpdateState) override { + if (BluetoothState::poweredOff == didUpdateState) { + // stop(); + } + if (BluetoothState::poweredOn == didUpdateState) { + // start(); + } + if (BluetoothState::unsupported == didUpdateState) { + for (auto& delegate : delegates) { + delegate->sensor(SensorType::BLE, SensorState::unavailable); + } + } + } private: - class Impl; - std::unique_ptr mImpl; // unique as this is handled internally for all platforms by Herald -}; - -template -class ConcreteBLEReceiver : public BLEReceiver, public HeraldProtocolV1Provider, public std::enable_shared_from_this> { -public: - ConcreteBLEReceiver(ContextT& ctx, BluetoothStateManager& bluetoothStateManager, - std::shared_ptr payloadDataSupplier, std::shared_ptr bleDatabase); - ConcreteBLEReceiver(const ConcreteBLEReceiver& from) = delete; - ConcreteBLEReceiver(ConcreteBLEReceiver&& from) = delete; - ~ConcreteBLEReceiver(); - // Coordination overrides - Since v1.2-beta3 - std::optional> coordinationProvider() override; + // Internal API private methods here too - bool immediateSend(Data data, const TargetIdentifier& targetIdentifier) override; - bool immediateSendAll(Data data) override; + // Data members hidden by PIMPL - // Sensor overrides - void add(const std::shared_ptr& delegate) override; - void start() override; - void stop() override; + ConcreteBLEDatabase& database; + BluetoothStateManager& stateManager; + TransmitterT& transmitter; + ReceiverT& receiver; - // Herald V1 protocol provider overrides - // C++17 CALLBACK VERSION:- - // void openConnection(const TargetIdentifier& toTarget, const HeraldConnectionCallback& connCallback) override; - // void closeConnection(const TargetIdentifier& toTarget, const HeraldConnectionCallback& connCallback) override; - // void serviceDiscovery(Activity, CompletionCallback) override; - // void readPayload(Activity, CompletionCallback) override; - // void immediateSend(Activity, CompletionCallback) override; - // void immediateSendAll(Activity, CompletionCallback) override; + std::vector> delegates; - // NON C++17 VERSION:- - bool openConnection(const TargetIdentifier& toTarget) override; - bool closeConnection(const TargetIdentifier& toTarget) override; - void restartScanningAndAdvertising() override; - std::optional serviceDiscovery(Activity) override; - std::optional readPayload(Activity) override; - std::optional immediateSend(Activity) override; - std::optional immediateSendAll(Activity) override; - -private: - class Impl; - std::shared_ptr mImpl; // shared to allow static callbacks to be bound -}; - -template -class ConcreteBLETransmitter : public BLETransmitter, public std::enable_shared_from_this> { -public: - ConcreteBLETransmitter(ContextT& ctx, BluetoothStateManager& bluetoothStateManager, - std::shared_ptr payloadDataSupplier, std::shared_ptr bleDatabase); - ConcreteBLETransmitter(const ConcreteBLETransmitter& from) = delete; - ConcreteBLETransmitter(ConcreteBLETransmitter&& from) = delete; - ~ConcreteBLETransmitter(); - - // Coordination overrides - Since v1.2-beta3 - std::optional> coordinationProvider() override; + std::shared_ptr> coordinator; - // Sensor overrides - void add(const std::shared_ptr& delegate) override; - void start() override; - void stop() override; + bool addedSelfAsDelegate; -private: - class Impl; - std::shared_ptr mImpl; // shared to allow static callbacks to be bound + HLOGGER(ContextT); }; } // end namespace diff --git a/herald/include/herald/ble/ble_concrete_database.h b/herald/include/herald/ble/ble_concrete_database.h new file mode 100644 index 0000000..e7a0401 --- /dev/null +++ b/herald/include/herald/ble/ble_concrete_database.h @@ -0,0 +1,330 @@ +// Copyright 2020-2021 Herald Project Contributors +// SPDX-License-Identifier: Apache-2.0 +// + +#ifndef HERALD_BLE_CONCRETE_DATABASE_H +#define HERALD_BLE_CONCRETE_DATABASE_H + +#include "ble_database.h" +#include "ble_receiver.h" +#include "ble_sensor.h" +#include "ble_transmitter.h" +#include "ble_concrete.h" +#include "ble_protocols.h" +#include "bluetooth_state_manager.h" +#include "ble_device_delegate.h" +#include "filter/ble_advert_parser.h" +#include "../payload/payload_data_supplier.h" +#include "../context.h" +#include "../data/sensor_logger.h" +#include "ble_sensor_configuration.h" +#include "ble_coordinator.h" +#include "../datatype/bluetooth_state.h" + +#include +#include +#include +#include + +namespace herald { +namespace ble { + +using namespace herald::datatype; +using namespace herald::ble::filter; +using namespace herald::payload; + + +/// \brief Provides a callable that assists in ordering for most recently updated BLEDevice +struct last_updated_descending { + bool operator()(const std::shared_ptr& a, const std::shared_ptr& b) { + return a->timeIntervalSinceLastUpdate() > b->timeIntervalSinceLastUpdate(); // opposite order + } +}; + +template +class ConcreteBLEDatabase : public BLEDatabase, public BLEDeviceDelegate, public std::enable_shared_from_this> { +public: + ConcreteBLEDatabase(ContextT& context) + : ctx(context), + delegates(), + devices() + HLOGGERINIT(context,"herald","ConcreteBLEDatabase") + { + ; + } + + ConcreteBLEDatabase(const ConcreteBLEDatabase& from) = delete; + ConcreteBLEDatabase(ConcreteBLEDatabase&& from) = delete; + + ~ConcreteBLEDatabase() = default; + + // BLE Database overrides + + void add(const std::shared_ptr& delegate) override { + delegates.push_back(delegate); + } + + // Creation overrides + std::shared_ptr device(const BLEMacAddress& mac, const Data& advert/*, const RSSI& rssi*/) override { + // Check by MAC first + TargetIdentifier targetIdentifier((Data)mac); + auto results = matches([&targetIdentifier](const std::shared_ptr& d) { + return d->identifier() == targetIdentifier; + }); + if (results.size() != 0) { + // HTDBG("DEVICE ALREADY KNOWN BY MAC"); + // Assume advert details are known already + return results.front(); // TODO ensure we send back the latest, not just the first match + // res->rssi(rssi); + // return res; + } + + // Now check by pseudo mac + auto segments = BLEAdvertParser::extractSegments(advert,0); + // HTDBG("segments:-"); + // HTDBG(std::to_string(segments.size())); + auto manuData = BLEAdvertParser::extractManufacturerData(segments); + auto heraldDataSegments = BLEAdvertParser::extractHeraldManufacturerData(manuData); + // HTDBG("herald data segments:-"); + // HTDBG(std::to_string(heraldDataSegments.size())); + // auto device = db->device(bleMacAddress); // For most devices this will suffice + + // TODO Check for public herald service in ADV_IND packet - shown if an Android device, wearable or beacon in zephyr + // auto serviceData128 = BLEAdvertParser::extractServiceUUID128Data(segments); + // bool hasHeraldService = false; + // for (auto& service : serviceData128) { + // if (service.uuid == heraldUuidData) { + // hasHeraldService = true; + // HTDBG("FOUND DEVICE ADVERTISING HERALD SERVICE"); + // device->operatingSystem(BLEDeviceOperatingSystem::android); + // } + // } + + + if (0 != heraldDataSegments.size()) { + // HTDBG("Found Herald Android pseudo device address in advert"); + // Try to FIND by pseudo first + BLEMacAddress pseudo(heraldDataSegments.front()); + auto samePseudo = matches([&pseudo](const std::shared_ptr& d) { + return d->pseudoDeviceAddress() == pseudo; + }); + if (0 != samePseudo.size()) { + // HTDBG("FOUND EXISTING DEVICE BY PSEUDO"); + return samePseudo.front(); + } + // HTDBG("CREATING NEW DEVICE BY MAC AND PSEUDO ONLY"); + // Now create new device with mac and pseudo + auto newDevice = device(mac,pseudo); + assignAdvertData(newDevice,std::move(segments), manuData); + // newDevice->rssi(rssi); + return newDevice; + } + + // HTDBG("CREATING NEW DEVICE BY MAC ONLY"); + + // Now create a device just from a mac + auto newDevice = device(targetIdentifier); + // HTDBG("Got new device"); + assignAdvertData(newDevice,std::move(segments), manuData); + // newDevice->rssi(rssi); + // HTDBG("Assigned advert data"); + return newDevice; + } + + std::shared_ptr device(const BLEMacAddress& mac, const BLEMacAddress& pseudo) override { + auto samePseudo = matches([&pseudo](const std::shared_ptr& d) { + return d->pseudoDeviceAddress() == pseudo; + }); + if (0 == samePseudo.size()) { + auto ptr = device(TargetIdentifier((Data)pseudo)); + ptr->pseudoDeviceAddress(pseudo); + return ptr; + } + // get most recent and clone, then attach + auto comp = last_updated_descending(); + std::sort(samePseudo.begin(),samePseudo.end(), comp); // functional style + auto newDevice = std::make_shared(*samePseudo.front()); // copy ctor used + // TODO support calling card + // auto toShare = shareDataAcrossDevices(pseudo); + // if (toShare.has_value()) { + // newDevice.payloadData(toShare); + // } + + // Has pseudo address so must be android + newDevice->operatingSystem(BLEDeviceOperatingSystem::android); + + // register new device discovery date + newDevice->registerDiscovery(Date()); + + devices.push_back(newDevice); + for (auto delegate : delegates) { + delegate->bleDatabaseDidCreate(newDevice); + } + return newDevice; + } + + std::shared_ptr device(const BLEMacAddress& mac) override { + return device(TargetIdentifier((Data)mac)); + } + + std::shared_ptr device(const PayloadData& payloadData) override { + auto results = matches([&payloadData](const std::shared_ptr& d) { + auto payload = d->payloadData(); + if (!payload.has_value()) { + return false; + } + return (*payload)==payloadData; + }); + if (results.size() != 0) { + return results.front(); // TODO ensure we send back the latest, not just the first match + } + std::shared_ptr newDevice = std::make_shared( + TargetIdentifier(payloadData), this->shared_from_this()); + devices.push_back(newDevice); + for (auto delegate : delegates) { + delegate->bleDatabaseDidCreate(newDevice); + } + return newDevice; + } + std::shared_ptr device(const TargetIdentifier& targetIdentifier) override { + auto results = matches([&targetIdentifier](const std::shared_ptr& d) { + return d->identifier() == targetIdentifier; + }); + if (results.size() != 0) { + return results.front(); // TODO ensure we send back the latest, not just the first match + } + HTDBG("New target identified: {}",(std::string)targetIdentifier); + std::shared_ptr newDevice = std::make_shared( + targetIdentifier, this->shared_from_this()); + devices.push_back(newDevice); + for (auto delegate : delegates) { + delegate->bleDatabaseDidCreate(newDevice); + } + return newDevice; + } + + // Introspection overrides + std::size_t size() const override { + return devices.size(); + } + + std::vector> matches( + const std::function&)>& matcher) const override { + std::vector> results; + // in the absence of copy_if in C++20... Just copies the pointers not the objects + for (auto& d : devices) { + if (matcher(d)) { + results.push_back(d); + } + } + return results; + } + + // std::vector> devices() const override; + + /// Cannot name a function delete in C++. remove is common. + void remove(const TargetIdentifier& targetIdentifier) override { + auto found = std::find_if(devices.begin(),devices.end(), + [&targetIdentifier](std::shared_ptr& d) -> bool { + return d->identifier() == targetIdentifier; + } + ); + if (found != devices.end()) { + std::shared_ptr toRemove = *found; + devices.erase(found); + for (auto& delegate : delegates) { + delegate->bleDatabaseDidDelete(toRemove); + } + } + } + + // std::optional payloadSharingData(const std::shared_ptr& peer) override; + + // BLE Device Delegate overrides + void device(const std::shared_ptr& device, BLEDeviceAttribute didUpdate) override { + // TODO update any internal DB state as necessary (E.g. deletion) + for (auto& delegate : delegates) { + delegate->bleDatabaseDidUpdate(device, didUpdate); // TODO verify this is the right onward call + } + } + +private: + void assignAdvertData(std::shared_ptr& newDevice, std::vector&& toMove, + const std::vector& manuData) + { + newDevice->advertData(std::move(toMove)); + + // If it's an apple device, check to see if its on our ignore list + auto appleDataSegments = BLEAdvertParser::extractAppleManufacturerSegments(manuData); + if (0 != appleDataSegments.size()) { + HTDBG("Found apple device"); + // HTDBG((std::string)mac); + newDevice->operatingSystem(BLEDeviceOperatingSystem::ios); + // TODO see if we should ignore this Apple device + // TODO abstract these out eventually in to BLEDevice class + bool ignore = false; + /* + "^10....04", + "^10....14", + "^0100000000000000000000000000000000", + "^05","^07","^09", + "^00","^1002","^06","^08","^03","^0C","^0D","^0F","^0E","^0B" + */ + for (auto& segment : appleDataSegments) { + HTDBG(segment.data.hexEncodedString()); + switch (segment.type) { + case 0x00: + case 0x05: + case 0x07: + case 0x09: + case 0x06: + case 0x08: + case 0x03: + case 0x0C: + case 0x0D: + case 0x0F: + case 0x0E: + case 0x0B: + ignore = true; + break; + case 0x10: + // check if second is 02 + if (segment.data.at(0) == std::byte(0x02)) { + ignore = true; + } else { + // Check 3rd data bit for 14 or 04 + if (segment.data.at(2) == std::byte(0x04) || segment.data.at(2) == std::byte(0x14)) { + ignore = true; + } + } + break; + default: + break; + } + } + if (ignore) { + HTDBG(" - Ignoring Apple device due to Apple data filter"); + newDevice->ignore(true); + } else { + // Perform GATT service discovery to check for Herald service + // NOTE: Happens from Connection request (handled by BLE Coordinator) + HTDBG(" - Unknown apple device... Logging so we can discover services later"); + } + } else { + // Not a Herald android or any iOS - so inspect later (beacon or wearable) + HTDBG("Unknown non Herald device - inspecting (might be a venue beacon or wearable)"); + // HTDBG((std::string)mac); + } + } + + ContextT& ctx; + std::vector> delegates; + std::vector> devices; + + HLOGGER(ContextT); +}; + +} +} + +#endif \ No newline at end of file diff --git a/herald/include/herald/ble/zephyr/concrete_ble_receiver.h b/herald/include/herald/ble/zephyr/concrete_ble_receiver.h new file mode 100644 index 0000000..fc2425d --- /dev/null +++ b/herald/include/herald/ble/zephyr/concrete_ble_receiver.h @@ -0,0 +1,82 @@ +// Copyright 2020-2021 Herald Project Contributors +// SPDX-License-Identifier: Apache-2.0 +// + +#ifndef HERALD_BLE_CONCRETE_RECEIVER_H +#define HERALD_BLE_CONCRETE_RECEIVER_H + +#include "../ble_database.h" +#include "../ble_receiver.h" +#include "../ble_sensor.h" +#include "../ble_transmitter.h" +#include "../ble_concrete.h" +#include "../ble_protocols.h" +#include "../bluetooth_state_manager.h" +#include "../ble_device_delegate.h" +#include "../filter/ble_advert_parser.h" +#include "../../payload/payload_data_supplier.h" +#include "../../context.h" +#include "../../data/sensor_logger.h" +#include "../ble_sensor_configuration.h" +#include "../ble_coordinator.h" +#include "../../datatype/bluetooth_state.h" + +#include +#include +#include +#include + +namespace herald { +namespace ble { + +using namespace herald::datatype; +using namespace herald::ble::filter; +using namespace herald::payload; + +template +class ConcreteBLEReceiver : public BLEReceiver, public HeraldProtocolV1Provider, public std::enable_shared_from_this> { +public: + ConcreteBLEReceiver(ContextT& ctx, BluetoothStateManager& bluetoothStateManager, + std::shared_ptr payloadDataSupplier, std::shared_ptr bleDatabase); + ConcreteBLEReceiver(const ConcreteBLEReceiver& from) = delete; + ConcreteBLEReceiver(ConcreteBLEReceiver&& from) = delete; + ~ConcreteBLEReceiver(); + + // Coordination overrides - Since v1.2-beta3 + std::optional> coordinationProvider() override; + + bool immediateSend(Data data, const TargetIdentifier& targetIdentifier) override; + bool immediateSendAll(Data data) override; + + // Sensor overrides + void add(const std::shared_ptr& delegate) override; + void start() override; + void stop() override; + + // Herald V1 protocol provider overrides + // C++17 CALLBACK VERSION:- + // void openConnection(const TargetIdentifier& toTarget, const HeraldConnectionCallback& connCallback) override; + // void closeConnection(const TargetIdentifier& toTarget, const HeraldConnectionCallback& connCallback) override; + // void serviceDiscovery(Activity, CompletionCallback) override; + // void readPayload(Activity, CompletionCallback) override; + // void immediateSend(Activity, CompletionCallback) override; + // void immediateSendAll(Activity, CompletionCallback) override; + + // NON C++17 VERSION:- + bool openConnection(const TargetIdentifier& toTarget) override; + bool closeConnection(const TargetIdentifier& toTarget) override; + void restartScanningAndAdvertising() override; + std::optional serviceDiscovery(Activity) override; + std::optional readPayload(Activity) override; + std::optional immediateSend(Activity) override; + std::optional immediateSendAll(Activity) override; + +private: + class Impl; + std::shared_ptr mImpl; // shared to allow static callbacks to be bound +}; + +} +} + +#endif \ No newline at end of file diff --git a/herald/include/herald/ble/zephyr/concrete_ble_transmitter.h b/herald/include/herald/ble/zephyr/concrete_ble_transmitter.h new file mode 100644 index 0000000..d41fad8 --- /dev/null +++ b/herald/include/herald/ble/zephyr/concrete_ble_transmitter.h @@ -0,0 +1,63 @@ +// Copyright 2020-2021 Herald Project Contributors +// SPDX-License-Identifier: Apache-2.0 +// + +#ifndef HERALD_BLE_CONCRETE_TRANSMITTER_H +#define HERALD_BLE_CONCRETE_TRANSMITTER_H + +#include "../ble_database.h" +#include "../ble_receiver.h" +#include "../ble_sensor.h" +#include "../ble_transmitter.h" +#include "../ble_concrete.h" +#include "../ble_protocols.h" +#include "../bluetooth_state_manager.h" +#include "../ble_device_delegate.h" +#include "../filter/ble_advert_parser.h" +#include "../../payload/payload_data_supplier.h" +#include "../../context.h" +#include "../../data/sensor_logger.h" +#include "../ble_sensor_configuration.h" +#include "../ble_coordinator.h" +#include "../../datatype/bluetooth_state.h" + +#include +#include +#include +#include + +namespace herald { +namespace ble { + +using namespace herald::datatype; +using namespace herald::ble::filter; +using namespace herald::payload; + +// TODO zephyr internal functions called by template + +template +class ConcreteBLETransmitter : public BLETransmitter, public std::enable_shared_from_this> { +public: + ConcreteBLETransmitter(ContextT& ctx, BluetoothStateManager& bluetoothStateManager, + std::shared_ptr payloadDataSupplier, std::shared_ptr bleDatabase); + ConcreteBLETransmitter(const ConcreteBLETransmitter& from) = delete; + ConcreteBLETransmitter(ConcreteBLETransmitter&& from) = delete; + ~ConcreteBLETransmitter(); + + // Coordination overrides - Since v1.2-beta3 + std::optional> coordinationProvider() override; + + // Sensor overrides + void add(const std::shared_ptr& delegate) override; + void start() override; + void stop() override; + +private: + class Impl; + std::shared_ptr mImpl; // shared to allow static callbacks to be bound +}; + +} +} + +#endif \ No newline at end of file diff --git a/herald/include/herald/data/sensor_logger.h b/herald/include/herald/data/sensor_logger.h index 3e3962e..a38013d 100644 --- a/herald/include/herald/data/sensor_logger.h +++ b/herald/include/herald/data/sensor_logger.h @@ -67,7 +67,7 @@ #if HERALD_LOG_LEVEL == 0 -#define HLOGGER /* No logger instance */ +#define HLOGGER(_ctxT) /* No logger instance */ #define HLOGGERINIT(...) /* No logger init */ #define HDBG(...) /* No debug log */ #define HERR(...) /* No error log */ @@ -80,7 +80,7 @@ #else -#define HLOGGER /* No logger instance */ +#define HLOGGER(_ctxT) /* No logger instance */ #define HLOGGERINIT(...) /* No logger init */ #define HDBG(...) /* No debug log */ #define HERR(...) /* No error log */ diff --git a/herald/include/herald/data/zephyr/zephyr_logging_sink.h b/herald/include/herald/data/zephyr/zephyr_logging_sink.h index f6f7f83..501b59b 100644 --- a/herald/include/herald/data/zephyr/zephyr_logging_sink.h +++ b/herald/include/herald/data/zephyr/zephyr_logging_sink.h @@ -5,11 +5,11 @@ #ifndef ZEPHYR_LOGGING_SINK_H #define ZEPHYR_LOGGING_SINK_H -#include "herald/data/sensor_logger.h" +#include "../sensor_logger.h" // NOTE: Link Herald to the Zephyr logging system // Set HERALD_LOG_LEVEL=4 for debug in CMake using add_definitions(-DHERALD_LOG_LEVEL=4 ) -// Defaults to 0 (OFF) - see zephyr_context.h +// Defaults to 0 (OFF) - see herald/data/zephyr/zephyr_logging_sink.h #include namespace herald { @@ -21,8 +21,11 @@ namespace data { class ZephyrLoggingSink { public: - log(const std::string& subsystem,const std::string& category, - SensorLoggerLevel level, const std::string& message); + ZephyrLoggingSink() = default; + ~ZephyrLoggingSink() = default; + + void log(const std::string& subsystem,const std::string& category, + SensorLoggerLevel level, const std::string& message); }; } // end namespace diff --git a/herald/include/herald/payload/simple/simple_payload_data_supplier.h b/herald/include/herald/payload/simple/simple_payload_data_supplier.h index 2ac2221..47cc028 100644 --- a/herald/include/herald/payload/simple/simple_payload_data_supplier.h +++ b/herald/include/herald/payload/simple/simple_payload_data_supplier.h @@ -11,7 +11,7 @@ #include "../extended/extended_data.h" #include "../../context.h" #include "../../datatype/payload_timestamp.h" -#include "herald/data/sensor_logger.h" +#include "../../data/sensor_logger.h" #include #include diff --git a/herald/include/herald/sensor_array.h b/herald/include/herald/sensor_array.h index 4993ce1..8d91411 100644 --- a/herald/include/herald/sensor_array.h +++ b/herald/include/herald/sensor_array.h @@ -5,21 +5,30 @@ #ifndef SENSOR_ARRAY_H #define SENSOR_ARRAY_H +#include "data/sensor_logger.h" #include "sensor_delegate.h" #include "sensor.h" #include "context.h" #include "payload/payload_data_supplier.h" +#include "datatype/payload_timestamp.h" #include "datatype/data.h" #include "datatype/target_identifier.h" #include "datatype/payload_data.h" +#include "ble/ble_concrete.h" +#include "engine/coordinator.h" #include +#include +#include #include namespace herald { +using namespace ble; +using namespace data; using namespace datatype; using namespace payload; +using namespace engine; /// \brief Manages all Sensors and sensor delegates for Herald /// @@ -35,27 +44,81 @@ template class SensorArray : public Sensor { public: /// \brief Takes ownership of payloadDataSupplier (std::move) - SensorArray(ContextT& ctx, std::shared_ptr payloadDataSupplier); - ~SensorArray(); + SensorArray(ContextT& ctx, std::shared_ptr payloadDataSupplier) + : mContext(ctx), + mPayloadDataSupplier(payloadDataSupplier), + mSensorArray(), + engine(ctx), + deviceDescription("") + HLOGGERINIT(mContext, "Sensor", "SensorArray") + { + } + ~SensorArray() = default; + + // TODO add immediate send support back in when template mechanism determined // SENSOR ARRAY METHODS - bool immediateSend(Data data, const TargetIdentifier& targetIdentifier); - bool immediateSendAll(Data data); + // bool immediateSend(Data data, const TargetIdentifier& targetIdentifier) { + // return concrete->immediateSend(data, targetIdentifier); + // } + + // bool immediateSendAll(Data data) { + // return concrete->immediateSendAll(data); + // } + + std::optional payloadData() { + return mPayloadDataSupplier->payload(PayloadTimestamp(),nullptr); + } - std::optional payloadData(); + /// \brief Adds a new sensor to the array, and add its coordination provider to the engine + void add(const std::shared_ptr& sensor) { + mSensorArray.push_back(sensor); // adds in links to BLE transmitter, receiver + engine.add(sensor); + } // SENSOR OVERRIDES - void add(const std::shared_ptr& delegate) override; - void start() override; - void stop() override; - std::optional> coordinationProvider() override; + void add(const std::shared_ptr& delegate) override { + for (auto& sensor: mSensorArray) { + sensor->add(delegate); + } + } + + void start() override { + for (auto& sensor: mSensorArray) { + sensor->start(); + } + engine.start(); + } + + void stop() override { + engine.stop(); + for (auto& sensor: mSensorArray) { + sensor->stop(); + } + } + + std::optional> coordinationProvider() override { + return {}; + } /// \brief Scheduling activities from external OS thread wakes - Since v1.2-beta3 - void iteration(const TimeInterval sinceLastCompleted); + void iteration(const TimeInterval sinceLastCompleted) { + // TODO ensure this works for continuous evaluation with minimal overhead or battery + engine.iteration(); + } private: - class Impl; - std::unique_ptr mImpl; // PIMPL IDIOM + // Initialised on entry to Impl constructor:- + ContextT& mContext; + std::shared_ptr mPayloadDataSupplier; + std::vector> mSensorArray; + + Coordinator engine; + + // Not initialised (and thus optional):- + std::string deviceDescription; + + HLOGGER(ContextT); }; diff --git a/herald/include/herald/zephyr_context.h b/herald/include/herald/zephyr_context.h index 53132d3..b24179b 100644 --- a/herald/include/herald/zephyr_context.h +++ b/herald/include/herald/zephyr_context.h @@ -13,6 +13,7 @@ #define CONFIG_HERALD_LOG_LEVEL 0 #endif +#include "data/zephyr/zephyr_logging_sink.h" #include "data/sensor_logger.h" #include @@ -28,6 +29,7 @@ namespace herald { using namespace herald::ble; +using namespace herald::data; /// \brief Internal zephyr namespace DO NOT USE - API MAY CHANGE WITHOUT WARNING namespace zephyrinternal { diff --git a/herald/src/ble/concrete_ble_sensor.cpp b/herald/src/ble/concrete_ble_sensor.cpp index fe8611c..27782e2 100644 --- a/herald/src/ble/concrete_ble_sensor.cpp +++ b/herald/src/ble/concrete_ble_sensor.cpp @@ -19,240 +19,240 @@ namespace ble { using namespace herald::datatype; -template -class ConcreteBLESensor::Impl { -public: - Impl(ContextT& ctx, - BluetoothStateManager& bluetoothStateManager, - std::shared_ptr payloadDataSupplier); - ~Impl(); +// template +// class ConcreteBLESensor::Impl { +// public: +// Impl(ContextT& ctx, +// BluetoothStateManager& bluetoothStateManager, +// std::shared_ptr payloadDataSupplier); +// ~Impl(); - // Internal API private methods here too +// // Internal API private methods here too - // Data members hidden by PIMPL +// // Data members hidden by PIMPL - std::shared_ptr> database; - BluetoothStateManager& stateManager; - std::shared_ptr> transmitter; - std::shared_ptr> receiver; +// std::shared_ptr> database; +// BluetoothStateManager& stateManager; +// std::shared_ptr> transmitter; +// std::shared_ptr> receiver; - std::vector> delegates; +// std::vector> delegates; - std::shared_ptr> coordinator; +// std::shared_ptr> coordinator; - bool addedSelfAsDelegate; +// bool addedSelfAsDelegate; - HLOGGER(ContextT); -}; +// HLOGGER(ContextT); +// }; -template -ConcreteBLESensor::Impl::Impl(ContextT& ctx, - BluetoothStateManager& bluetoothStateManager, - std::shared_ptr payloadDataSupplier) - : database(std::make_shared(ctx)), - stateManager(bluetoothStateManager), - transmitter(std::make_shared( - ctx, bluetoothStateManager, payloadDataSupplier, database) - ), - receiver(std::make_shared( - ctx, bluetoothStateManager, payloadDataSupplier, database) - ), - delegates(), - coordinator(std::make_shared>(ctx, database, receiver)), - addedSelfAsDelegate(false) - HLOGGERINIT(ctx,"sensor","ConcreteBLESensor") -{ - ; -} +// template +// ConcreteBLESensor::Impl::Impl(ContextT& ctx, +// BluetoothStateManager& bluetoothStateManager, +// std::shared_ptr payloadDataSupplier) +// : database(std::make_shared(ctx)), +// stateManager(bluetoothStateManager), +// transmitter(std::make_shared( +// ctx, bluetoothStateManager, payloadDataSupplier, database) +// ), +// receiver(std::make_shared( +// ctx, bluetoothStateManager, payloadDataSupplier, database) +// ), +// delegates(), +// coordinator(std::make_shared>(ctx, database, receiver)), +// addedSelfAsDelegate(false) +// HLOGGERINIT(ctx,"sensor","ConcreteBLESensor") +// { +// ; +// } -template -ConcreteBLESensor::Impl::~Impl() -{ - ; -} +// template +// ConcreteBLESensor::Impl::~Impl() +// { +// ; +// } -template -ConcreteBLESensor::ConcreteBLESensor(ContextT& ctx, - BluetoothStateManager& bluetoothStateManager, - std::shared_ptr payloadDataSupplier) - : mImpl(std::make_unique(ctx,bluetoothStateManager,payloadDataSupplier)) -{ - ; -} +// template +// ConcreteBLESensor::ConcreteBLESensor(ContextT& ctx, +// BluetoothStateManager& bluetoothStateManager, +// std::shared_ptr payloadDataSupplier) +// : mImpl(std::make_unique(ctx,bluetoothStateManager,payloadDataSupplier)) +// { +// ; +// } -template -ConcreteBLESensor::~ConcreteBLESensor() -{ - ; -} +// template +// ConcreteBLESensor::~ConcreteBLESensor() +// { +// ; +// } -template -std::optional> -ConcreteBLESensor::coordinationProvider() -{ - // Only return this if we support scanning - if (BLESensorConfiguration::scanningEnabled) { - HDBG("Providing a BLECoordinationProvider"); - return std::optional>(mImpl->coordinator); - } - HDBG("Scanning not supported - so not returning a BLECoordinationProvider"); - return {}; -} +// template +// std::optional> +// ConcreteBLESensor::coordinationProvider() +// { +// // Only return this if we support scanning +// if (BLESensorConfiguration::scanningEnabled) { +// HDBG("Providing a BLECoordinationProvider"); +// return std::optional>(mImpl->coordinator); +// } +// HDBG("Scanning not supported - so not returning a BLECoordinationProvider"); +// return {}; +// } -template -bool -ConcreteBLESensor::immediateSend(Data data, const TargetIdentifier& targetIdentifier) -{ - return mImpl->receiver->immediateSend(data,targetIdentifier); -} +// template +// bool +// ConcreteBLESensor::immediateSend(Data data, const TargetIdentifier& targetIdentifier) +// { +// return mImpl->receiver->immediateSend(data,targetIdentifier); +// } -template -bool -ConcreteBLESensor::immediateSendAll(Data data) -{ - return mImpl->receiver->immediateSendAll(data); -} +// template +// bool +// ConcreteBLESensor::immediateSendAll(Data data) +// { +// return mImpl->receiver->immediateSendAll(data); +// } -// Sensor overrides -template -void -ConcreteBLESensor::add(const std::shared_ptr& delegate) -{ - mImpl->delegates.push_back(delegate); - // add all delegates to receiver and transmitter too? - mImpl->receiver->add(delegate); - mImpl->transmitter->add(delegate); - // TODO what about duplicates? -} +// // Sensor overrides +// template +// void +// ConcreteBLESensor::add(const std::shared_ptr& delegate) +// { +// mImpl->delegates.push_back(delegate); +// // add all delegates to receiver and transmitter too? +// mImpl->receiver->add(delegate); +// mImpl->transmitter->add(delegate); +// // TODO what about duplicates? +// } -template -void -ConcreteBLESensor::start() -{ - if (!mImpl->addedSelfAsDelegate) { - mImpl->stateManager.add(shared_from_this()); // FAILS IF USED IN THE CTOR - DO NOT DO THIS FROM CTOR - mImpl->database->add(shared_from_this()); - mImpl->addedSelfAsDelegate = true; - } - mImpl->transmitter->start(); - mImpl->receiver->start(); - for (auto& delegate : mImpl->delegates) { - delegate->sensor(SensorType::BLE, SensorState::on); - } -} +// template +// void +// ConcreteBLESensor::start() +// { +// if (!mImpl->addedSelfAsDelegate) { +// mImpl->stateManager.add(this->shared_from_this()); // FAILS IF USED IN THE CTOR - DO NOT DO THIS FROM CTOR +// mImpl->database->add(this->shared_from_this()); +// mImpl->addedSelfAsDelegate = true; +// } +// mImpl->transmitter->start(); +// mImpl->receiver->start(); +// for (auto& delegate : mImpl->delegates) { +// delegate->sensor(SensorType::BLE, SensorState::on); +// } +// } -template -void -ConcreteBLESensor::stop() -{ - mImpl->transmitter->stop(); - mImpl->receiver->stop(); - for (auto& delegate : mImpl->delegates) { - delegate->sensor(SensorType::BLE, SensorState::off); - } -} +// template +// void +// ConcreteBLESensor::stop() +// { +// mImpl->transmitter->stop(); +// mImpl->receiver->stop(); +// for (auto& delegate : mImpl->delegates) { +// delegate->sensor(SensorType::BLE, SensorState::off); +// } +// } -template -// Database overrides -void -ConcreteBLESensor::bleDatabaseDidCreate(const std::shared_ptr& device) -{ - for (auto& delegate : mImpl->delegates) { - delegate->sensor(SensorType::BLE, device->identifier()); // didDetect - } -} +// template +// // Database overrides +// void +// ConcreteBLESensor::bleDatabaseDidCreate(const std::shared_ptr& device) +// { +// for (auto& delegate : mImpl->delegates) { +// delegate->sensor(SensorType::BLE, device->identifier()); // didDetect +// } +// } -template -void -ConcreteBLESensor::bleDatabaseDidUpdate(const std::shared_ptr& device, - const BLEDeviceAttribute attribute) -{ - switch (attribute) { - case BLEDeviceAttribute::rssi: { - auto rssi = device->rssi(); - if (rssi.has_value()) { - double rssiValue = (double)rssi->intValue(); - auto prox = Proximity{.unit=ProximityMeasurementUnit::RSSI, .value=rssiValue}; - for (auto& delegate: mImpl->delegates) { - delegate->sensor(SensorType::BLE, - prox, - device->identifier() - ); // didMeasure - } - // also payload with rssi - auto payload = device->payloadData(); - if (payload.has_value()) { - for (auto& delegate: mImpl->delegates) { - delegate->sensor(SensorType::BLE, - prox, - device->identifier(), - *payload - ); // didMeasure withPayload - } - } - } - break; - } - case BLEDeviceAttribute::payloadData: { - auto payload = device->payloadData(); - if (payload.has_value()) { - for (auto& delegate: mImpl->delegates) { - delegate->sensor(SensorType::BLE, - *payload, - device->identifier() - ); // didReadPayload - } - // also payload with rssi - auto rssi = device->rssi(); - if (rssi.has_value()) { - double rssiValue = (double)rssi->intValue(); - auto prox = Proximity{.unit=ProximityMeasurementUnit::RSSI, .value=rssiValue}; - for (auto& delegate: mImpl->delegates) { - delegate->sensor(SensorType::BLE, - prox, - device->identifier(), - *payload - ); // didMeasure withPayload - } - } - } - break; - } - default: { - ; // do nothing - } - } -} +// template +// void +// ConcreteBLESensor::bleDatabaseDidUpdate(const std::shared_ptr& device, +// const BLEDeviceAttribute attribute) +// { +// switch (attribute) { +// case BLEDeviceAttribute::rssi: { +// auto rssi = device->rssi(); +// if (rssi.has_value()) { +// double rssiValue = (double)rssi->intValue(); +// auto prox = Proximity{.unit=ProximityMeasurementUnit::RSSI, .value=rssiValue}; +// for (auto& delegate: mImpl->delegates) { +// delegate->sensor(SensorType::BLE, +// prox, +// device->identifier() +// ); // didMeasure +// } +// // also payload with rssi +// auto payload = device->payloadData(); +// if (payload.has_value()) { +// for (auto& delegate: mImpl->delegates) { +// delegate->sensor(SensorType::BLE, +// prox, +// device->identifier(), +// *payload +// ); // didMeasure withPayload +// } +// } +// } +// break; +// } +// case BLEDeviceAttribute::payloadData: { +// auto payload = device->payloadData(); +// if (payload.has_value()) { +// for (auto& delegate: mImpl->delegates) { +// delegate->sensor(SensorType::BLE, +// *payload, +// device->identifier() +// ); // didReadPayload +// } +// // also payload with rssi +// auto rssi = device->rssi(); +// if (rssi.has_value()) { +// double rssiValue = (double)rssi->intValue(); +// auto prox = Proximity{.unit=ProximityMeasurementUnit::RSSI, .value=rssiValue}; +// for (auto& delegate: mImpl->delegates) { +// delegate->sensor(SensorType::BLE, +// prox, +// device->identifier(), +// *payload +// ); // didMeasure withPayload +// } +// } +// } +// break; +// } +// default: { +// ; // do nothing +// } +// } +// } -template -void -ConcreteBLESensor::bleDatabaseDidDelete(const std::shared_ptr& device) -{ - ; // TODO just log this // TODO determine if to pass this on too -} +// template +// void +// ConcreteBLESensor::bleDatabaseDidDelete(const std::shared_ptr& device) +// { +// ; // TODO just log this // TODO determine if to pass this on too +// } -// Bluetooth state manager delegate overrides -template -void -ConcreteBLESensor::bluetoothStateManager(BluetoothState didUpdateState) -{ - if (BluetoothState::poweredOff == didUpdateState) { - // stop(); - } - if (BluetoothState::poweredOn == didUpdateState) { - // start(); - } - if (BluetoothState::unsupported == didUpdateState) { - for (auto& delegate : mImpl->delegates) { - delegate->sensor(SensorType::BLE, SensorState::unavailable); - } - } -} +// // Bluetooth state manager delegate overrides +// template +// void +// ConcreteBLESensor::bluetoothStateManager(BluetoothState didUpdateState) +// { +// if (BluetoothState::poweredOff == didUpdateState) { +// // stop(); +// } +// if (BluetoothState::poweredOn == didUpdateState) { +// // start(); +// } +// if (BluetoothState::unsupported == didUpdateState) { +// for (auto& delegate : mImpl->delegates) { +// delegate->sensor(SensorType::BLE, SensorState::unavailable); +// } +// } +// } } } \ No newline at end of file diff --git a/herald/src/ble/zephyr/concrete_ble_receiver.cpp b/herald/src/ble/zephyr/concrete_ble_receiver.cpp index d1e06d9..db0d4c8 100644 --- a/herald/src/ble/zephyr/concrete_ble_receiver.cpp +++ b/herald/src/ble/zephyr/concrete_ble_receiver.cpp @@ -333,9 +333,10 @@ namespace zephyrinternal { } -class ConcreteBLEReceiver::Impl : public herald::zephyrinternal::Callbacks { +template +class ConcreteBLEReceiver::Impl : public herald::zephyrinternal::Callbacks { public: - Impl(std::shared_ptr ctx, std::shared_ptr bluetoothStateManager, + Impl(ContextT& ctx, BluetoothStateManager& bluetoothStateManager, std::shared_ptr payloadDataSupplier, std::shared_ptr bleDatabase); ~Impl(); @@ -368,8 +369,8 @@ class ConcreteBLEReceiver::Impl : public herald::zephyrinternal::Callbacks { void stopScanning(); void gatt_discover(struct bt_conn *conn); - std::shared_ptr m_context; - std::shared_ptr m_stateManager; + ContextT& m_context; + BluetoothStateManager& m_stateManager; std::shared_ptr m_pds; std::shared_ptr db; @@ -378,13 +379,14 @@ class ConcreteBLEReceiver::Impl : public herald::zephyrinternal::Callbacks { std::map connectionStates; bool isScanning; - HLOGGER; + HLOGGER(ContextT); }; -ConcreteBLEReceiver::Impl::Impl(std::shared_ptr ctx, std::shared_ptr bluetoothStateManager, +template +ConcreteBLEReceiver::Impl::Impl(ContextT& ctx, BluetoothStateManager& bluetoothStateManager, std::shared_ptr payloadDataSupplier, std::shared_ptr bleDatabase) - : m_context(std::static_pointer_cast(ctx)), // Herald API guarantees this to be safe + : m_context(ctx), // Herald API guarantees this to be safe m_stateManager(bluetoothStateManager), m_pds(payloadDataSupplier), db(bleDatabase), @@ -396,7 +398,8 @@ ConcreteBLEReceiver::Impl::Impl(std::shared_ptr ctx, std::shared_ptr +ConcreteBLEReceiver::Impl::~Impl() { ; } @@ -424,8 +427,9 @@ ConcreteBLEReceiver::Impl::~Impl() // return {}; // } +template ConnectedDeviceState& -ConcreteBLEReceiver::Impl::findOrCreateState(const TargetIdentifier& forTarget) +ConcreteBLEReceiver::Impl::findOrCreateState(const TargetIdentifier& forTarget) { auto iter = connectionStates.find(forTarget); if (connectionStates.end() != iter) { @@ -435,8 +439,9 @@ ConcreteBLEReceiver::Impl::findOrCreateState(const TargetIdentifier& forTarget) // return connectionStates.find(forTarget)->second; } +template ConnectedDeviceState& -ConcreteBLEReceiver::Impl::findOrCreateStateByConnection(struct bt_conn *conn, bool remoteInstigated) +ConcreteBLEReceiver::Impl::findOrCreateStateByConnection(struct bt_conn *conn, bool remoteInstigated) { for (auto& [key, value] : connectionStates) { if (value.connection == conn) { @@ -453,8 +458,9 @@ ConcreteBLEReceiver::Impl::findOrCreateStateByConnection(struct bt_conn *conn, b return result.first->second; } +template void -ConcreteBLEReceiver::Impl::removeState(const TargetIdentifier& forTarget) +ConcreteBLEReceiver::Impl::removeState(const TargetIdentifier& forTarget) { auto iter = connectionStates.find(forTarget); if (connectionStates.end() != iter) { @@ -462,8 +468,9 @@ ConcreteBLEReceiver::Impl::removeState(const TargetIdentifier& forTarget) } } +template void -ConcreteBLEReceiver::Impl::stopScanning() +ConcreteBLEReceiver::Impl::stopScanning() { if (isScanning) { isScanning = false; @@ -471,8 +478,9 @@ ConcreteBLEReceiver::Impl::stopScanning() } } +template void -ConcreteBLEReceiver::Impl::startScanning() +ConcreteBLEReceiver::Impl::startScanning() { if (isScanning) { return; @@ -486,8 +494,9 @@ ConcreteBLEReceiver::Impl::startScanning() isScanning = true; } +template void -ConcreteBLEReceiver::Impl::scan_cb(const bt_addr_le_t *addr, int8_t rssi, uint8_t adv_type, +ConcreteBLEReceiver::Impl::scan_cb(const bt_addr_le_t *addr, int8_t rssi, uint8_t adv_type, struct net_buf_simple *buf) { // identify device by both MAC and potential pseudoDeviceAddress @@ -514,8 +523,9 @@ ConcreteBLEReceiver::Impl::scan_cb(const bt_addr_le_t *addr, int8_t rssi, uint8_ device->rssi(RSSI(rssi)); } +template void -ConcreteBLEReceiver::Impl::gatt_discover(struct bt_conn *conn) +ConcreteBLEReceiver::Impl::gatt_discover(struct bt_conn *conn) { HTDBG("Attempting GATT service discovery"); int err; @@ -531,15 +541,17 @@ ConcreteBLEReceiver::Impl::gatt_discover(struct bt_conn *conn) HTDBG("Service discovery succeeded... now do something with it in the callback!"); } +template void -ConcreteBLEReceiver::Impl::le_param_updated(struct bt_conn *conn, uint16_t interval, +ConcreteBLEReceiver::Impl::le_param_updated(struct bt_conn *conn, uint16_t interval, uint16_t latency, uint16_t timeout) { HTDBG("le param updated called"); } +template void -ConcreteBLEReceiver::Impl::connected(struct bt_conn *conn, uint8_t err) +ConcreteBLEReceiver::Impl::connected(struct bt_conn *conn, uint8_t err) { HTDBG("**************** Zephyr connection callback. Mac of connected:"); @@ -589,8 +601,9 @@ ConcreteBLEReceiver::Impl::connected(struct bt_conn *conn, uint8_t err) } +template void -ConcreteBLEReceiver::Impl::disconnected(struct bt_conn *conn, uint8_t reason) +ConcreteBLEReceiver::Impl::disconnected(struct bt_conn *conn, uint8_t reason) { HTDBG("********** Zephyr disconnection callback. Mac of disconnected:"); @@ -623,8 +636,9 @@ ConcreteBLEReceiver::Impl::disconnected(struct bt_conn *conn, uint8_t reason) // Discovery callbacks +template void -ConcreteBLEReceiver::Impl::discovery_completed_cb(struct bt_gatt_dm *dm, +ConcreteBLEReceiver::Impl::discovery_completed_cb(struct bt_gatt_dm *dm, void *context) { HTDBG("The GATT discovery procedure succeeded"); @@ -704,8 +718,9 @@ ConcreteBLEReceiver::Impl::discovery_completed_cb(struct bt_gatt_dm *dm, device->services(serviceList); } +template void -ConcreteBLEReceiver::Impl::discovery_service_not_found_cb(struct bt_conn *conn, +ConcreteBLEReceiver::Impl::discovery_service_not_found_cb(struct bt_conn *conn, void *context) { HTDBG("The service could not be found during the discovery. Ignoring device:"); @@ -718,8 +733,9 @@ ConcreteBLEReceiver::Impl::discovery_service_not_found_cb(struct bt_conn *conn, device->ignore(true); } +template void -ConcreteBLEReceiver::Impl::discovery_error_found_cb(struct bt_conn *conn, +ConcreteBLEReceiver::Impl::discovery_error_found_cb(struct bt_conn *conn, int err, void *context) { @@ -728,27 +744,28 @@ ConcreteBLEReceiver::Impl::discovery_error_found_cb(struct bt_conn *conn, // TODO decide if we should ignore the device here, or just keep trying } +template uint8_t -ConcreteBLEReceiver::Impl::gatt_read_cb(struct bt_conn *conn, uint8_t err, +ConcreteBLEReceiver::Impl::gatt_read_cb(struct bt_conn *conn, uint8_t err, struct bt_gatt_read_params *params, const void *data, uint16_t length) - { - // Fetch state for this element - ConnectedDeviceState& state = findOrCreateStateByConnection(conn); - if (NULL == data) { - HTDBG("Finished reading CHAR read payload:-"); - HTDBG(state.readPayload.hexEncodedString()); - - // Set final read payload (triggers success callback on observer) - db->device(state.target)->payloadData(state.readPayload); - - return 0; - } +{ + // Fetch state for this element + ConnectedDeviceState& state = findOrCreateStateByConnection(conn); + if (NULL == data) { + HTDBG("Finished reading CHAR read payload:-"); + HTDBG(state.readPayload.hexEncodedString()); + + // Set final read payload (triggers success callback on observer) + db->device(state.target)->payloadData(state.readPayload); - state.readPayload.append((const uint8_t*)data,0,length); - return length; + return 0; } + state.readPayload.append((const uint8_t*)data,0,length); + return length; +} + @@ -756,32 +773,37 @@ ConcreteBLEReceiver::Impl::gatt_read_cb(struct bt_conn *conn, uint8_t err, -ConcreteBLEReceiver::ConcreteBLEReceiver(std::shared_ptr ctx, std::shared_ptr bluetoothStateManager, +template +ConcreteBLEReceiver::ConcreteBLEReceiver(ContextT& ctx, BluetoothStateManager& bluetoothStateManager, std::shared_ptr payloadDataSupplier, std::shared_ptr bleDatabase) : mImpl(std::make_shared(ctx,bluetoothStateManager,payloadDataSupplier,bleDatabase)) { ; } -ConcreteBLEReceiver::~ConcreteBLEReceiver() +template +ConcreteBLEReceiver::~ConcreteBLEReceiver() { ; } +template std::optional> -ConcreteBLEReceiver::coordinationProvider() +ConcreteBLEReceiver::coordinationProvider() { return {}; // we don't provide this, ConcreteBLESensor provides this. We provide HeraldV1ProtocolProvider } +template void -ConcreteBLEReceiver::add(const std::shared_ptr& delegate) +ConcreteBLEReceiver::add(const std::shared_ptr& delegate) { mImpl->delegates.push_back(delegate); } +template void -ConcreteBLEReceiver::start() +ConcreteBLEReceiver::start() { HDBG("ConcreteBLEReceiver::start"); if (!BLESensorConfiguration::scanningEnabled) { @@ -810,8 +832,9 @@ ConcreteBLEReceiver::start() HDBG("ConcreteBLEReceiver::start completed successfully"); } +template void -ConcreteBLEReceiver::stop() +ConcreteBLEReceiver::stop() { HDBG("ConcreteBLEReceiver::stop"); if (!BLESensorConfiguration::scanningEnabled) { @@ -829,14 +852,16 @@ ConcreteBLEReceiver::stop() } +template bool -ConcreteBLEReceiver::immediateSend(Data data, const TargetIdentifier& targetIdentifier) +ConcreteBLEReceiver::immediateSend(Data data, const TargetIdentifier& targetIdentifier) { return false; // TODO implement this } +template bool -ConcreteBLEReceiver::immediateSendAll(Data data) +ConcreteBLEReceiver::immediateSendAll(Data data) { return false; // TODO implement this } @@ -847,8 +872,9 @@ ConcreteBLEReceiver::immediateSendAll(Data data) // void // ConcreteBLEReceiver::openConnection(const TargetIdentifier& toTarget, const HeraldConnectionCallback& connCallback) // { +template bool -ConcreteBLEReceiver::openConnection(const TargetIdentifier& toTarget) +ConcreteBLEReceiver::openConnection(const TargetIdentifier& toTarget) { HDBG("openConnection"); @@ -1060,8 +1086,9 @@ ConcreteBLEReceiver::openConnection(const TargetIdentifier& toTarget) // callback(activity,{}); // } +template bool -ConcreteBLEReceiver::closeConnection(const TargetIdentifier& toTarget) +ConcreteBLEReceiver::closeConnection(const TargetIdentifier& toTarget) { HDBG("closeConnection call for ADDR:-"); ConnectedDeviceState& state = mImpl->findOrCreateState(toTarget); @@ -1087,8 +1114,9 @@ ConcreteBLEReceiver::closeConnection(const TargetIdentifier& toTarget) return true; // remote instigated the connection - keep it open and inform caller } +template void -ConcreteBLEReceiver::restartScanningAndAdvertising() +ConcreteBLEReceiver::restartScanningAndAdvertising() { // Print out current list of devices and their info if (!mImpl->connectionStates.empty()) { @@ -1147,8 +1175,9 @@ ConcreteBLEReceiver::restartScanningAndAdvertising() mImpl->m_context->getAdvertiser().startAdvertising(); } +template std::optional -ConcreteBLEReceiver::serviceDiscovery(Activity activity) +ConcreteBLEReceiver::serviceDiscovery(Activity activity) { auto currentTargetOpt = std::get<1>(activity.prerequisites.front()); if (!currentTargetOpt.has_value()) { @@ -1181,20 +1210,23 @@ ConcreteBLEReceiver::serviceDiscovery(Activity activity) return {}; } +template std::optional -ConcreteBLEReceiver::readPayload(Activity activity) +ConcreteBLEReceiver::readPayload(Activity activity) { return {}; } +template std::optional -ConcreteBLEReceiver::immediateSend(Activity activity) +ConcreteBLEReceiver::immediateSend(Activity activity) { return {}; } +template std::optional -ConcreteBLEReceiver::immediateSendAll(Activity activity) +ConcreteBLEReceiver::immediateSendAll(Activity activity) { return {}; } diff --git a/herald/src/ble/zephyr/concrete_ble_transmitter.cpp b/herald/src/ble/zephyr/concrete_ble_transmitter.cpp index 7175895..ffd52a8 100644 --- a/herald/src/ble/zephyr/concrete_ble_transmitter.cpp +++ b/herald/src/ble/zephyr/concrete_ble_transmitter.cpp @@ -73,17 +73,18 @@ namespace zephyrinternal { } } -class ConcreteBLETransmitter::Impl { +template +class ConcreteBLETransmitter::Impl { public: - Impl(Context& ctx, std::shared_ptr bluetoothStateManager, + Impl(ContextT& ctx, BluetoothStateManager& bluetoothStateManager, std::shared_ptr payloadDataSupplier, std::shared_ptr bleDatabase); ~Impl(); void startAdvertising(); void stopAdvertising(); - Context& m_context; - std::shared_ptr m_stateManager; + ContextT& m_context; + BluetoothStateManager& m_stateManager; std::shared_ptr m_pds; std::shared_ptr m_db; @@ -91,7 +92,7 @@ class ConcreteBLETransmitter::Impl { bool isAdvertising; - HLOGGER; + HLOGGER(ContextT); }; /* Herald Service Variables */ @@ -217,8 +218,9 @@ static struct bt_data ad[] = { // #endif -ConcreteBLETransmitter::Impl::Impl( - Context& ctx, std::shared_ptr bluetoothStateManager, +template +ConcreteBLETransmitter::Impl::Impl( + ContextT& ctx, BluetoothStateManager& bluetoothStateManager, std::shared_ptr payloadDataSupplier, std::shared_ptr bleDatabase) : m_context(ctx), m_stateManager(bluetoothStateManager), @@ -232,13 +234,15 @@ ConcreteBLETransmitter::Impl::Impl( latestPds = m_pds.get(); } -ConcreteBLETransmitter::Impl::~Impl() +template +ConcreteBLETransmitter::Impl::~Impl() { latestPds = NULL; } +template void -ConcreteBLETransmitter::Impl::startAdvertising() +ConcreteBLETransmitter::Impl::startAdvertising() { // HTDBG("startAdvertising called"); if (!BLESensorConfiguration::advertisingEnabled) { @@ -279,8 +283,9 @@ ConcreteBLETransmitter::Impl::startAdvertising() isAdvertising = true; } +template void -ConcreteBLETransmitter::Impl::stopAdvertising() +ConcreteBLETransmitter::Impl::stopAdvertising() { // HTDBG("stopAdvertising called"); if (!BLESensorConfiguration::advertisingEnabled) { @@ -308,8 +313,9 @@ ConcreteBLETransmitter::Impl::stopAdvertising() -ConcreteBLETransmitter::ConcreteBLETransmitter( - Context& ctx, std::shared_ptr bluetoothStateManager, +template +ConcreteBLETransmitter::ConcreteBLETransmitter( + ContextT& ctx, BluetoothStateManager& bluetoothStateManager, std::shared_ptr payloadDataSupplier, std::shared_ptr bleDatabase) : mImpl(std::make_shared(ctx,bluetoothStateManager, payloadDataSupplier,bleDatabase)) @@ -317,27 +323,31 @@ ConcreteBLETransmitter::ConcreteBLETransmitter( ; } -ConcreteBLETransmitter::~ConcreteBLETransmitter() +template +ConcreteBLETransmitter::~ConcreteBLETransmitter() { // stop(); // stops using m_addr } +template std::optional> -ConcreteBLETransmitter::coordinationProvider() +ConcreteBLETransmitter::coordinationProvider() { return {}; } +template void -ConcreteBLETransmitter::add(const std::shared_ptr& delegate) +ConcreteBLETransmitter::add(const std::shared_ptr& delegate) { mImpl->delegates.push_back(delegate); } // 6. Implement any additional transmitter functionality, as required +template void -ConcreteBLETransmitter::start() +ConcreteBLETransmitter::start() { HDBG("ConcreteBLETransmitter::start"); if (!BLESensorConfiguration::advertisingEnabled) { @@ -360,8 +370,9 @@ ConcreteBLETransmitter::start() mImpl->startAdvertising(); } +template void -ConcreteBLETransmitter::stop() +ConcreteBLETransmitter::stop() { HDBG("ConcreteBLETransmitter::stop"); if (!BLESensorConfiguration::advertisingEnabled) { diff --git a/herald/src/data/zephyr/zephyr_logging_sink.cpp b/herald/src/data/zephyr/zephyr_logging_sink.cpp index ee6508c..7c06f51 100644 --- a/herald/src/data/zephyr/zephyr_logging_sink.cpp +++ b/herald/src/data/zephyr/zephyr_logging_sink.cpp @@ -2,16 +2,13 @@ // SPDX-License-Identifier: Apache-2.0 // -#include "herald/data/sensor_logger.h" +#include "herald/data/zephyr/zephyr_logging_sink.h" #include namespace herald { namespace data { -ZephyrLoggingSink::ZephyrLoggingSink() = default; -ZephyrLoggingSink::~ZephyrLoggingSink() = default; - void ZephyrLoggingSink::log(const std::string& subsystem,const std::string& category, SensorLoggerLevel level, const std::string& message) diff --git a/herald/src/sensor_array.cpp b/herald/src/sensor_array.cpp index 370c616..2aa8044 100644 --- a/herald/src/sensor_array.cpp +++ b/herald/src/sensor_array.cpp @@ -10,155 +10,142 @@ #include "herald/ble/ble_concrete.h" #include "herald/engine/coordinator.h" -#include -#include -#include -#include +// #include +// #include +// #include +// #include namespace herald { -using namespace ble; -using namespace data; -using namespace datatype; -using namespace payload; -using namespace engine; - -template -class SensorArray::Impl { -public: - Impl(ContextT& ctx, std::shared_ptr payloadDataSupplier); - ~Impl(); - - // Initialised on entry to Impl constructor:- - ContextT& mContext; - std::shared_ptr mPayloadDataSupplier; - std::vector> mSensorArray; - - std::shared_ptr> concrete; - - Coordinator engine; - - // Not initialised (and thus optional):- - std::string deviceDescription; - - HLOGGER(ContextT); -}; - -template -SensorArray::Impl::Impl(ContextT& ctx, std::shared_ptr payloadDataSupplier) - : mContext(ctx), - mPayloadDataSupplier(payloadDataSupplier), - mSensorArray(), - concrete(std::make_shared>(mContext, mContext.getBluetoothStateManager(), - mPayloadDataSupplier)), - engine(ctx), - deviceDescription("") - HLOGGERINIT(mContext, "Sensor", "SensorArray") -{ - // PayloadTimestamp pts; // now - // mPayloadData = mPayloadDataSupplier->payload(pts); - // add(std::make_shared(mContext, "contacts.csv")); - // add(std::make_shared(mContext, "statistics.csv", payloadData)); - // add(std::make_shared(mContext, "statistics_didRead.csv", payloadData)); - // add(std::make_shared(mContext,"detection.csv", payloadData)); - // mBatteryLog = std::make_shared(mContext, "battery.csv"); - - mSensorArray.push_back(concrete); // adds in links to BLE transmitter, receiver - engine.add(concrete); - - // deviceDescription = ""; // TODO get the real device description - - // NOTE THE FOLLOWING LINE CAUSES ZEPHYR APPS TO NOT EXECUTE - COUT ISSUE? - // TODO test this now logging on zephyr is reliable - //mLogger.info("DEVICE (payload={},description={})", "nil", deviceDescription); -} - -template -SensorArray::Impl::~Impl() -{ - ; -} - - - - - - -/// Takes ownership of payloadDataSupplier (std::move) -template -SensorArray::SensorArray(ContextT& ctx, std::shared_ptr payloadDataSupplier) - : mImpl(std::make_unique(ctx,payloadDataSupplier)) -{ - ; -} - -template -SensorArray::~SensorArray() -{ - ; -} - -// SENSOR ARRAY METHODS -template -bool -SensorArray::immediateSend(Data data, const TargetIdentifier& targetIdentifier) { - return mImpl->concrete->immediateSend(data, targetIdentifier); -} - -template -bool -SensorArray::immediateSendAll(Data data) { - return mImpl->concrete->immediateSendAll(data); -} - -template -std::optional -SensorArray::payloadData() { - return mImpl->mPayloadDataSupplier->payload(PayloadTimestamp(),nullptr); -} - -// SENSOR OVERRIDES -template -void -SensorArray::add(const std::shared_ptr& delegate) { - for (auto& sensor: mImpl->mSensorArray) { - sensor->add(delegate); - } -} - -template -void -SensorArray::start() { - for (auto& sensor: mImpl->mSensorArray) { - sensor->start(); - } - mImpl->engine.start(); -} - -template -void -SensorArray::stop() { - mImpl->engine.stop(); - for (auto& sensor: mImpl->mSensorArray) { - sensor->stop(); - } -} - -template -std::optional> -SensorArray::coordinationProvider() -{ - return {}; -} - -// Periodic actions -template -void -SensorArray::iteration(const TimeInterval sinceLastCompleted) -{ - // TODO ensure this works for continuous evaluation with minimal overhead or battery - mImpl->engine.iteration(); -} +// using namespace ble; +// using namespace data; +// using namespace datatype; +// using namespace payload; +// using namespace engine; + +// template +// class SensorArray::Impl { +// public: +// Impl(ContextT& ctx, std::shared_ptr payloadDataSupplier); +// ~Impl(); + +// }; + +// template +// SensorArray::Impl::Impl(ContextT& ctx, std::shared_ptr payloadDataSupplier) +// : mContext(ctx), +// mPayloadDataSupplier(payloadDataSupplier), +// mSensorArray(), +// concrete(std::make_shared>(mContext, mContext.getBluetoothStateManager(), +// mPayloadDataSupplier)), +// engine(ctx), +// deviceDescription("") +// HLOGGERINIT(mContext, "Sensor", "SensorArray") +// { +// // PayloadTimestamp pts; // now +// // mPayloadData = mPayloadDataSupplier->payload(pts); +// // add(std::make_shared(mContext, "contacts.csv")); +// // add(std::make_shared(mContext, "statistics.csv", payloadData)); +// // add(std::make_shared(mContext, "statistics_didRead.csv", payloadData)); +// // add(std::make_shared(mContext,"detection.csv", payloadData)); +// // mBatteryLog = std::make_shared(mContext, "battery.csv"); + +// mSensorArray.push_back(concrete); // adds in links to BLE transmitter, receiver +// engine.add(concrete); + +// // deviceDescription = ""; // TODO get the real device description + +// // NOTE THE FOLLOWING LINE CAUSES ZEPHYR APPS TO NOT EXECUTE - COUT ISSUE? +// // TODO test this now logging on zephyr is reliable +// //mLogger.info("DEVICE (payload={},description={})", "nil", deviceDescription); +// } + +// template +// SensorArray::Impl::~Impl() +// { +// ; +// } + + + + + + +// /// Takes ownership of payloadDataSupplier (std::move) +// template +// SensorArray::SensorArray(ContextT& ctx, std::shared_ptr payloadDataSupplier) +// : mImpl(std::make_unique(ctx,payloadDataSupplier)) +// { +// ; +// } + +// template +// SensorArray::~SensorArray() +// { +// ; +// } + +// // SENSOR ARRAY METHODS +// template +// bool +// SensorArray::immediateSend(Data data, const TargetIdentifier& targetIdentifier) { +// return mImpl->concrete->immediateSend(data, targetIdentifier); +// } + +// template +// bool +// SensorArray::immediateSendAll(Data data) { +// return mImpl->concrete->immediateSendAll(data); +// } + +// template +// std::optional +// SensorArray::payloadData() { +// return mImpl->mPayloadDataSupplier->payload(PayloadTimestamp(),nullptr); +// } + +// // SENSOR OVERRIDES +// template +// void +// SensorArray::add(const std::shared_ptr& delegate) { +// for (auto& sensor: mImpl->mSensorArray) { +// sensor->add(delegate); +// } +// } + +// template +// void +// SensorArray::start() { +// for (auto& sensor: mImpl->mSensorArray) { +// sensor->start(); +// } +// mImpl->engine.start(); +// } + +// template +// void +// SensorArray::stop() { +// mImpl->engine.stop(); +// for (auto& sensor: mImpl->mSensorArray) { +// sensor->stop(); +// } +// } + +// template +// std::optional> +// SensorArray::coordinationProvider() +// { +// return {}; +// } + +// // Periodic actions +// template +// void +// SensorArray::iteration(const TimeInterval sinceLastCompleted) +// { +// // TODO ensure this works for continuous evaluation with minimal overhead or battery +// mImpl->engine.iteration(); +// } } // end namespace diff --git a/herald/src/zephyr_context.cpp b/herald/src/zephyr_context.cpp index fe6d4d4..891f20e 100644 --- a/herald/src/zephyr_context.cpp +++ b/herald/src/zephyr_context.cpp @@ -95,7 +95,7 @@ ZephyrContextProvider::ZephyrContextProvider() ; } -ZephyrContextProvider::~Context() = default; +ZephyrContextProvider::~ZephyrContextProvider() = default; ZephyrLoggingSink& ZephyrContextProvider::getLoggingSink() @@ -106,7 +106,7 @@ ZephyrContextProvider::getLoggingSink() BluetoothStateManager& ZephyrContextProvider::getBluetoothStateManager() { - return *this + return *this; } void From 0c6d8ba0067f2324cae8563df13834f727577a3a Mon Sep 17 00:00:00 2001 From: Adam Fowler Date: Wed, 31 Mar 2021 08:55:29 +0100 Subject: [PATCH 20/28] Tests passing with coordinator using references Signed-off-by: Adam Fowler --- herald-tests/CMakeLists.txt | 5 + herald-tests/blecoordinator-tests.cpp | 134 +++++++----------- herald-tests/bledevice-tests.cpp | 78 +++++----- herald-tests/coordinator-tests.cpp | 113 +++++++-------- herald-wearable/src/main.cpp | 2 +- herald/include/herald/ble/ble_concrete.h | 9 +- .../herald/ble/ble_concrete_database.h | 6 +- herald/include/herald/ble/ble_coordinator.h | 42 +++--- herald/include/herald/ble/ble_device.h | 2 +- herald/include/herald/engine/coordinator.h | 18 +-- herald/include/herald/sensor.h | 2 +- herald/include/herald/sensor_array.h | 2 +- herald/src/ble/ble_device.cpp | 20 +-- .../src/ble/zephyr/concrete_ble_receiver.cpp | 1 + .../ble/zephyr/concrete_ble_transmitter.cpp | 1 + herald/src/engine/coordinator.cpp | 4 +- 16 files changed, 212 insertions(+), 227 deletions(-) diff --git a/herald-tests/CMakeLists.txt b/herald-tests/CMakeLists.txt index ea7f785..c963cb4 100644 --- a/herald-tests/CMakeLists.txt +++ b/herald-tests/CMakeLists.txt @@ -45,4 +45,9 @@ include_directories(${herald_SOURCE_DIR}) target_link_libraries(herald-tests PRIVATE herald) # Threads::Threads +#add_compile_options(-Wl,--stack,100000000) +#set_target_properties(herald-tests PROPERTIES LINK_FLAGS -Wl,--stack,10000000) +#set_target_properties(herald-tests PROPERTIES LINK_FLAGS /STACK:10000000) +add_compile_options(/STACK:1000000000000) +set_target_properties(herald-tests PROPERTIES LINK_FLAGS /STACK:1000000000000) target_compile_features(herald-tests PRIVATE cxx_std_17) diff --git a/herald-tests/blecoordinator-tests.cpp b/herald-tests/blecoordinator-tests.cpp index 33e41c7..fb467b0 100644 --- a/herald-tests/blecoordinator-tests.cpp +++ b/herald-tests/blecoordinator-tests.cpp @@ -12,10 +12,10 @@ #include "herald/herald.h" -template +template class NoOpHeraldV1ProtocolProvider : public herald::ble::HeraldProtocolV1Provider { public: - NoOpHeraldV1ProtocolProvider(ContextT& context,std::shared_ptr bledb) + NoOpHeraldV1ProtocolProvider(ContextT& context,BLEDBT& bledb) : ctx(context) HLOGGERINIT(ctx,"TESTS","NoOpProvider") {} @@ -119,24 +119,21 @@ TEST_CASE("blecoordinator-ctor", "[coordinator][ctor][basic]") { DummyBluetoothStateManager dbsm; herald::Context ctx(dls,dbsm); // default context include using CT = typename herald::Context; - std::shared_ptr> db = - std::make_shared>(ctx); - std::shared_ptr> pp = - std::make_shared>(ctx,db); - std::shared_ptr> coord = - std::make_shared>(ctx,db,pp); - - std::vector provided = coord->connectionsProvided(); + herald::ble::ConcreteBLEDatabase db(ctx); + NoOpHeraldV1ProtocolProvider pp(ctx,db); + herald::ble::HeraldProtocolBLECoordinationProvider coord(ctx,db,pp); + + std::vector provided = coord.connectionsProvided(); REQUIRE(provided.size() == 1); // Herald BLE protocol auto prot = provided.front(); REQUIRE(prot == herald::engine::Features::HeraldBluetoothProtocolConnection); std::vector>> conns = - coord->requiredConnections(); + coord.requiredConnections(); REQUIRE(conns.size() == 0); - std::vector acts = coord->requiredActivities(); + std::vector acts = coord.requiredActivities(); REQUIRE(acts.size() == 0); } } @@ -148,20 +145,17 @@ TEST_CASE("blecoordinator-unseen-device", "[coordinator][unseen-device][basic]") DummyBluetoothStateManager dbsm; herald::Context ctx(dls,dbsm); // default context include using CT = typename herald::Context; - std::shared_ptr> db = - std::make_shared>(ctx); - std::shared_ptr> pp = - std::make_shared>(ctx,db); - std::shared_ptr> coord = - std::make_shared>(ctx,db,pp); + herald::ble::ConcreteBLEDatabase db(ctx); + NoOpHeraldV1ProtocolProvider pp(ctx,db); + herald::ble::HeraldProtocolBLECoordinationProvider coord(ctx,db,pp); herald::datatype::Data devMac1(std::byte(0x1d),6); herald::datatype::TargetIdentifier device1(devMac1); - std::shared_ptr devPtr1 = db->device(device1); + std::shared_ptr devPtr1 = db.device(device1); std::vector>> conns = - coord->requiredConnections(); + coord.requiredConnections(); REQUIRE(conns.size() == 1); auto firstConn = conns.front(); REQUIRE(std::get<0>(firstConn) == herald::engine::Features::HeraldBluetoothProtocolConnection); @@ -169,7 +163,7 @@ TEST_CASE("blecoordinator-unseen-device", "[coordinator][unseen-device][basic]") REQUIRE(std::get<2>(firstConn).has_value()); REQUIRE(std::get<2>(firstConn).value() == device1); - std::vector acts = coord->requiredActivities(); + std::vector acts = coord.requiredActivities(); REQUIRE(acts.size() == 1); // determine if herald only (read payload is at a later state) auto firstAct = acts.front(); REQUIRE(firstAct.prerequisites.size() == 1); @@ -183,16 +177,13 @@ TEST_CASE("blecoordinator-android-no-id", "[coordinator][android-no-id][basic]") DummyBluetoothStateManager dbsm; herald::Context ctx(dls,dbsm); // default context include using CT = typename herald::Context; - std::shared_ptr> db = - std::make_shared>(ctx); - std::shared_ptr> pp = - std::make_shared>(ctx,db); - std::shared_ptr> coord = - std::make_shared>(ctx,db,pp); + herald::ble::ConcreteBLEDatabase db(ctx); + NoOpHeraldV1ProtocolProvider pp(ctx,db); + herald::ble::HeraldProtocolBLECoordinationProvider coord(ctx,db,pp); herald::datatype::Data devMac1(std::byte(0x1d),6); herald::datatype::TargetIdentifier device1(devMac1); - std::shared_ptr devPtr1 = db->device(device1); + std::shared_ptr devPtr1 = db.device(device1); // Specify that some activity has already happened with the device std::vector heraldServiceList; @@ -202,7 +193,7 @@ TEST_CASE("blecoordinator-android-no-id", "[coordinator][android-no-id][basic]") std::vector>> conns = - coord->requiredConnections(); + coord.requiredConnections(); REQUIRE(conns.size() == 1); auto firstConn = conns.front(); REQUIRE(std::get<0>(firstConn) == herald::engine::Features::HeraldBluetoothProtocolConnection); @@ -210,7 +201,7 @@ TEST_CASE("blecoordinator-android-no-id", "[coordinator][android-no-id][basic]") REQUIRE(std::get<2>(firstConn).has_value()); REQUIRE(std::get<2>(firstConn).value() == device1); - std::vector acts = coord->requiredActivities(); + std::vector acts = coord.requiredActivities(); REQUIRE(acts.size() == 1); // just read payload (ID) auto firstAct = acts.front(); REQUIRE(firstAct.prerequisites.size() == 1); @@ -223,20 +214,17 @@ TEST_CASE("blecoordinator-two-mixed-no-id", "[coordinator][two-mixed-no-id][basi DummyBluetoothStateManager dbsm; herald::Context ctx(dls,dbsm); // default context include using CT = typename herald::Context; - std::shared_ptr> db = - std::make_shared>(ctx); - std::shared_ptr> pp = - std::make_shared>(ctx,db); - std::shared_ptr> coord = - std::make_shared>(ctx,db,pp); + herald::ble::ConcreteBLEDatabase db(ctx); + NoOpHeraldV1ProtocolProvider pp(ctx,db); + herald::ble::HeraldProtocolBLECoordinationProvider coord(ctx,db,pp); herald::datatype::Data devMac1(std::byte(0x1d),6); herald::datatype::TargetIdentifier device1(devMac1); - std::shared_ptr devPtr1 = db->device(device1); + std::shared_ptr devPtr1 = db.device(device1); herald::datatype::Data devMac2(std::byte(0x1f),6); herald::datatype::TargetIdentifier device2(devMac2); - std::shared_ptr devPtr2 = db->device(device2); + std::shared_ptr devPtr2 = db.device(device2); // Specify that some activity has already happened with the device std::vector heraldServiceList; @@ -248,7 +236,7 @@ TEST_CASE("blecoordinator-two-mixed-no-id", "[coordinator][two-mixed-no-id][basi std::vector>> conns = - coord->requiredConnections(); + coord.requiredConnections(); REQUIRE(conns.size() == 2); // connections to BOTH devices auto firstConn = conns.front(); REQUIRE(std::get<0>(firstConn) == herald::engine::Features::HeraldBluetoothProtocolConnection); @@ -256,7 +244,7 @@ TEST_CASE("blecoordinator-two-mixed-no-id", "[coordinator][two-mixed-no-id][basi REQUIRE(std::get<2>(firstConn).has_value()); REQUIRE(std::get<2>(firstConn).value() == device1); - std::vector acts = coord->requiredActivities(); + std::vector acts = coord.requiredActivities(); REQUIRE(acts.size() == 2); // just read payload (ID) for BOTH devices auto firstAct = acts.front(); REQUIRE(firstAct.prerequisites.size() == 1); @@ -269,16 +257,13 @@ TEST_CASE("blecoordinator-got-os-and-id", "[coordinator][got-os-and-id][basic]") DummyBluetoothStateManager dbsm; herald::Context ctx(dls,dbsm); // default context include using CT = typename herald::Context; - std::shared_ptr> db = - std::make_shared>(ctx); - std::shared_ptr> pp = - std::make_shared>(ctx,db); - std::shared_ptr> coord = - std::make_shared>(ctx,db,pp); + herald::ble::ConcreteBLEDatabase db(ctx); + NoOpHeraldV1ProtocolProvider pp(ctx,db); + herald::ble::HeraldProtocolBLECoordinationProvider coord(ctx,db,pp); herald::datatype::Data devMac1(std::byte(0x1d),6); herald::datatype::TargetIdentifier device1(devMac1); - std::shared_ptr devPtr1 = db->device(device1); + std::shared_ptr devPtr1 = db.device(device1); // Specify that some activity has already happened with the device std::vector heraldServiceList; @@ -289,10 +274,10 @@ TEST_CASE("blecoordinator-got-os-and-id", "[coordinator][got-os-and-id][basic]") std::vector>> conns = - coord->requiredConnections(); + coord.requiredConnections(); REQUIRE(conns.size() == 0); - std::vector acts = coord->requiredActivities(); + std::vector acts = coord.requiredActivities(); REQUIRE(acts.size() == 0); } } @@ -303,20 +288,17 @@ TEST_CASE("blecoordinator-got-two-at-different-states", "[coordinator][got-two-a DummyBluetoothStateManager dbsm; herald::Context ctx(dls,dbsm); // default context include using CT = typename herald::Context; - std::shared_ptr> db = - std::make_shared>(ctx); - std::shared_ptr> pp = - std::make_shared>(ctx,db); - std::shared_ptr> coord = - std::make_shared>(ctx,db,pp); + herald::ble::ConcreteBLEDatabase db(ctx); + NoOpHeraldV1ProtocolProvider pp(ctx,db); + herald::ble::HeraldProtocolBLECoordinationProvider coord(ctx,db,pp); herald::datatype::Data devMac1(std::byte(0x1d),6); herald::datatype::TargetIdentifier device1(devMac1); - std::shared_ptr devPtr1 = db->device(device1); + std::shared_ptr devPtr1 = db.device(device1); herald::datatype::Data devMac2(std::byte(0x1f),6); herald::datatype::TargetIdentifier device2(devMac2); - std::shared_ptr devPtr2 = db->device(device2); + std::shared_ptr devPtr2 = db.device(device2); // Specify that some activity has already happened with the device std::vector heraldServiceList; @@ -327,7 +309,7 @@ TEST_CASE("blecoordinator-got-two-at-different-states", "[coordinator][got-two-a std::vector>> conns = - coord->requiredConnections(); + coord.requiredConnections(); REQUIRE(conns.size() == 1); // just for ONE device auto firstConn = conns.front(); REQUIRE(std::get<0>(firstConn) == herald::engine::Features::HeraldBluetoothProtocolConnection); @@ -335,7 +317,7 @@ TEST_CASE("blecoordinator-got-two-at-different-states", "[coordinator][got-two-a REQUIRE(std::get<2>(firstConn).has_value()); REQUIRE(std::get<2>(firstConn).value() == device2); // device 2 only - std::vector acts = coord->requiredActivities(); + std::vector acts = coord.requiredActivities(); REQUIRE(acts.size() == 1); // just read payload (ID) for ONE device auto firstAct = acts.front(); REQUIRE(firstAct.prerequisites.size() == 1); @@ -349,16 +331,13 @@ TEST_CASE("blecoordinator-got-immediate-send-targeted", "[coordinator][got-immed DummyBluetoothStateManager dbsm; herald::Context ctx(dls,dbsm); // default context include using CT = typename herald::Context; - std::shared_ptr> db = - std::make_shared>(ctx); - std::shared_ptr> pp = - std::make_shared>(ctx,db); - std::shared_ptr> coord = - std::make_shared>(ctx,db,pp); + herald::ble::ConcreteBLEDatabase db(ctx); + NoOpHeraldV1ProtocolProvider pp(ctx,db); + herald::ble::HeraldProtocolBLECoordinationProvider coord(ctx,db,pp); herald::datatype::Data devMac1(std::byte(0x1d),6); herald::datatype::TargetIdentifier device1(devMac1); - std::shared_ptr devPtr1 = db->device(device1); + std::shared_ptr devPtr1 = db.device(device1); // Specify that some activity has already happened with the device std::vector heraldServiceList; @@ -371,10 +350,10 @@ TEST_CASE("blecoordinator-got-immediate-send-targeted", "[coordinator][got-immed std::vector>> conns = - coord->requiredConnections(); + coord.requiredConnections(); REQUIRE(conns.size() == 1); - std::vector acts = coord->requiredActivities(); + std::vector acts = coord.requiredActivities(); REQUIRE(acts.size() == 1); } } @@ -385,24 +364,21 @@ TEST_CASE("blecoordinator-got-three-at-different-states", "[coordinator][got-thr DummyBluetoothStateManager dbsm; herald::Context ctx(dls,dbsm); // default context include using CT = typename herald::Context; - std::shared_ptr> db = - std::make_shared>(ctx); - std::shared_ptr> pp = - std::make_shared>(ctx,db); - std::shared_ptr> coord = - std::make_shared>(ctx,db,pp); + herald::ble::ConcreteBLEDatabase db(ctx); + NoOpHeraldV1ProtocolProvider pp(ctx,db); + herald::ble::HeraldProtocolBLECoordinationProvider coord(ctx,db,pp); herald::datatype::Data devMac1(std::byte(0x1d),6); herald::datatype::TargetIdentifier device1(devMac1); - std::shared_ptr devPtr1 = db->device(device1); + std::shared_ptr devPtr1 = db.device(device1); herald::datatype::Data devMac2(std::byte(0x1f),6); herald::datatype::TargetIdentifier device2(devMac2); - std::shared_ptr devPtr2 = db->device(device2); + std::shared_ptr devPtr2 = db.device(device2); herald::datatype::Data devMac3(std::byte(0x09),6); herald::datatype::TargetIdentifier device3(devMac3); - std::shared_ptr devPtr3 = db->device(device3); + std::shared_ptr devPtr3 = db.device(device3); // Specify that some activity has already happened with the device std::vector heraldServiceList; @@ -420,7 +396,7 @@ TEST_CASE("blecoordinator-got-three-at-different-states", "[coordinator][got-thr std::vector>> conns = - coord->requiredConnections(); + coord.requiredConnections(); REQUIRE(conns.size() == 2); auto firstConn = conns.front(); REQUIRE(std::get<0>(firstConn) == herald::engine::Features::HeraldBluetoothProtocolConnection); @@ -432,7 +408,7 @@ TEST_CASE("blecoordinator-got-three-at-different-states", "[coordinator][got-thr REQUIRE(std::get<2>(secondConn).has_value()); REQUIRE(std::get<2>(secondConn).value() == device3); - std::vector acts = coord->requiredActivities(); + std::vector acts = coord.requiredActivities(); REQUIRE(acts.size() == 2); // just read payload (ID) for ONE device, and immediate send for another auto firstAct = acts.front(); REQUIRE(firstAct.prerequisites.size() == 1); diff --git a/herald-tests/bledevice-tests.cpp b/herald-tests/bledevice-tests.cpp index 780e28c..e3c2a9f 100644 --- a/herald-tests/bledevice-tests.cpp +++ b/herald-tests/bledevice-tests.cpp @@ -30,7 +30,7 @@ TEST_CASE("ble-device-ctor", "[ble][device][ctor]") { SECTION("ble-device-ctor") { herald::datatype::Data d{std::byte(9),6}; herald::datatype::TargetIdentifier id{d}; - std::shared_ptr delegate = std::make_shared(); + DummyBLEDeviceDelegate delegate; herald::ble::BLEDevice device(id,delegate); REQUIRE(device.identifier() == id); @@ -62,7 +62,7 @@ TEST_CASE("ble-device-update-state", "[ble][device][update][state]") { herald::datatype::Date now; herald::datatype::Date createdAt = now - herald::datatype::TimeInterval::minutes(1); // forces updated times to be greater than zero - std::shared_ptr delegate = std::make_shared(); + DummyBLEDeviceDelegate delegate; std::shared_ptr device = std::make_shared(id,delegate,createdAt); @@ -70,10 +70,10 @@ TEST_CASE("ble-device-update-state", "[ble][device][update][state]") { device->state(s); REQUIRE(device->state() == s); - REQUIRE(delegate->callbackCalled); - std::shared_ptr dev = delegate->dev.value(); + REQUIRE(delegate.callbackCalled); + std::shared_ptr dev = delegate.dev.value(); REQUIRE(dev == device); - REQUIRE(delegate->attr.value() == herald::ble::BLEDeviceAttribute::state); + REQUIRE(delegate.attr.value() == herald::ble::BLEDeviceAttribute::state); herald::datatype::TimeInterval lu = device->timeIntervalSinceLastUpdate(); REQUIRE(lu >= herald::datatype::TimeInterval::seconds(0)); @@ -89,7 +89,7 @@ TEST_CASE("ble-device-update-os", "[ble][device][update][os]") { herald::datatype::Date now; herald::datatype::Date createdAt = now - herald::datatype::TimeInterval::minutes(1); // forces updated times to be greater than zero - std::shared_ptr delegate = std::make_shared(); + DummyBLEDeviceDelegate delegate; std::shared_ptr device = std::make_shared(id,delegate,createdAt); @@ -100,10 +100,10 @@ TEST_CASE("ble-device-update-os", "[ble][device][update][os]") { REQUIRE(device->operatingSystem() == s); // delegates - REQUIRE(delegate->callbackCalled); - std::shared_ptr dev = delegate->dev.value(); + REQUIRE(delegate.callbackCalled); + std::shared_ptr dev = delegate.dev.value(); REQUIRE(dev == device); - REQUIRE(delegate->attr.value() == herald::ble::BLEDeviceAttribute::operatingSystem); + REQUIRE(delegate.attr.value() == herald::ble::BLEDeviceAttribute::operatingSystem); herald::datatype::TimeInterval lu = device->timeIntervalSinceLastUpdate(); REQUIRE(lu >= herald::datatype::TimeInterval::seconds(0)); @@ -119,7 +119,7 @@ TEST_CASE("ble-device-update-payload", "[ble][device][update][payload]") { herald::datatype::Date now; herald::datatype::Date createdAt = now - herald::datatype::TimeInterval::minutes(1); // forces updated times to be greater than zero - std::shared_ptr delegate = std::make_shared(); + DummyBLEDeviceDelegate delegate; std::shared_ptr device = std::make_shared(id,delegate,createdAt); @@ -133,10 +133,10 @@ TEST_CASE("ble-device-update-payload", "[ble][device][update][payload]") { REQUIRE(device->payloadData().value() == payload); // delegates - REQUIRE(delegate->callbackCalled); - std::shared_ptr dev = delegate->dev.value(); + REQUIRE(delegate.callbackCalled); + std::shared_ptr dev = delegate.dev.value(); REQUIRE(dev == device); - REQUIRE(delegate->attr.value() == herald::ble::BLEDeviceAttribute::payloadData); + REQUIRE(delegate.attr.value() == herald::ble::BLEDeviceAttribute::payloadData); herald::datatype::TimeInterval lu = device->timeIntervalSinceLastUpdate(); REQUIRE(lu >= herald::datatype::TimeInterval::seconds(0)); @@ -155,7 +155,7 @@ TEST_CASE("ble-device-update-immediatesenddata", "[ble][device][update][immediat herald::datatype::Date now; herald::datatype::Date createdAt = now - herald::datatype::TimeInterval::minutes(1); // forces updated times to be greater than zero - std::shared_ptr delegate = std::make_shared(); + DummyBLEDeviceDelegate delegate; std::shared_ptr device = std::make_shared(id,delegate,createdAt); @@ -168,10 +168,10 @@ TEST_CASE("ble-device-update-immediatesenddata", "[ble][device][update][immediat REQUIRE(device->immediateSendData().value() == isd); // delegates - REQUIRE(delegate->callbackCalled); - std::shared_ptr dev = delegate->dev.value(); + REQUIRE(delegate.callbackCalled); + std::shared_ptr dev = delegate.dev.value(); REQUIRE(dev == device); - REQUIRE(delegate->attr.value() == herald::ble::BLEDeviceAttribute::immediateSendData); + REQUIRE(delegate.attr.value() == herald::ble::BLEDeviceAttribute::immediateSendData); herald::datatype::TimeInterval lu = device->timeIntervalSinceLastUpdate(); REQUIRE(lu >= herald::datatype::TimeInterval::seconds(0)); @@ -187,7 +187,7 @@ TEST_CASE("ble-device-update-rssi", "[ble][device][update][rssi]") { herald::datatype::Date now; herald::datatype::Date createdAt = now - herald::datatype::TimeInterval::minutes(1); // forces updated times to be greater than zero - std::shared_ptr delegate = std::make_shared(); + DummyBLEDeviceDelegate delegate; std::shared_ptr device = std::make_shared(id,delegate,createdAt); @@ -199,10 +199,10 @@ TEST_CASE("ble-device-update-rssi", "[ble][device][update][rssi]") { REQUIRE(device->rssi().value() == rssi); // delegates - REQUIRE(delegate->callbackCalled); - std::shared_ptr dev = delegate->dev.value(); + REQUIRE(delegate.callbackCalled); + std::shared_ptr dev = delegate.dev.value(); REQUIRE(dev == device); - REQUIRE(delegate->attr.value() == herald::ble::BLEDeviceAttribute::rssi); + REQUIRE(delegate.attr.value() == herald::ble::BLEDeviceAttribute::rssi); herald::datatype::TimeInterval lu = device->timeIntervalSinceLastUpdate(); REQUIRE(lu >= herald::datatype::TimeInterval::seconds(0)); @@ -218,7 +218,7 @@ TEST_CASE("ble-device-update-txpower", "[ble][device][update][txpower]") { herald::datatype::Date now; herald::datatype::Date createdAt = now - herald::datatype::TimeInterval::minutes(1); // forces updated times to be greater than zero - std::shared_ptr delegate = std::make_shared(); + DummyBLEDeviceDelegate delegate; std::shared_ptr device = std::make_shared(id,delegate,createdAt); @@ -230,10 +230,10 @@ TEST_CASE("ble-device-update-txpower", "[ble][device][update][txpower]") { REQUIRE(device->txPower().value() == tx); // delegates - REQUIRE(delegate->callbackCalled); - std::shared_ptr dev = delegate->dev.value(); + REQUIRE(delegate.callbackCalled); + std::shared_ptr dev = delegate.dev.value(); REQUIRE(dev == device); - REQUIRE(delegate->attr.value() == herald::ble::BLEDeviceAttribute::txPower); + REQUIRE(delegate.attr.value() == herald::ble::BLEDeviceAttribute::txPower); herald::datatype::TimeInterval lu = device->timeIntervalSinceLastUpdate(); REQUIRE(lu >= herald::datatype::TimeInterval::seconds(0)); @@ -249,7 +249,7 @@ TEST_CASE("ble-device-update-pseudo", "[ble][device][update][pseudo]") { herald::datatype::Date now; herald::datatype::Date createdAt = now - herald::datatype::TimeInterval::minutes(1); // forces updated times to be greater than zero - std::shared_ptr delegate = std::make_shared(); + DummyBLEDeviceDelegate delegate; std::shared_ptr device = std::make_shared(id,delegate,createdAt); @@ -262,7 +262,7 @@ TEST_CASE("ble-device-update-pseudo", "[ble][device][update][pseudo]") { REQUIRE(device->pseudoDeviceAddress().value() == pseudo); // delegates - REQUIRE(!delegate->callbackCalled); + REQUIRE(!delegate.callbackCalled); herald::datatype::TimeInterval lu = device->timeIntervalSinceLastUpdate(); REQUIRE(lu != herald::datatype::TimeInterval::never()); // pseudo address counts as update @@ -277,7 +277,7 @@ TEST_CASE("ble-device-update-receiveOnly", "[ble][device][update][receiveOnly]") herald::datatype::Date now; herald::datatype::Date createdAt = now - herald::datatype::TimeInterval::minutes(1); // forces updated times to be greater than zero - std::shared_ptr delegate = std::make_shared(); + DummyBLEDeviceDelegate delegate; std::shared_ptr device = std::make_shared(id,delegate,createdAt); @@ -288,7 +288,7 @@ TEST_CASE("ble-device-update-receiveOnly", "[ble][device][update][receiveOnly]") REQUIRE(device->receiveOnly() == true); // delegates - REQUIRE(!delegate->callbackCalled); + REQUIRE(!delegate.callbackCalled); herald::datatype::TimeInterval lu = device->timeIntervalSinceLastUpdate(); REQUIRE(lu == herald::datatype::TimeInterval::zero()); @@ -303,7 +303,7 @@ TEST_CASE("ble-device-update-ignore", "[ble][device][update][ignore]") { herald::datatype::Date now; herald::datatype::Date createdAt = now - herald::datatype::TimeInterval::minutes(1); // forces updated times to be greater than zero - std::shared_ptr delegate = std::make_shared(); + DummyBLEDeviceDelegate delegate; std::shared_ptr device = std::make_shared(id,delegate,createdAt); @@ -314,7 +314,7 @@ TEST_CASE("ble-device-update-ignore", "[ble][device][update][ignore]") { REQUIRE(device->ignore() == true); // delegates - REQUIRE(!delegate->callbackCalled); + REQUIRE(!delegate.callbackCalled); herald::datatype::TimeInterval lu = device->timeIntervalSinceLastUpdate(); REQUIRE(lu == herald::datatype::TimeInterval::zero()); @@ -329,7 +329,7 @@ TEST_CASE("ble-device-update-payloadchar", "[ble][device][update][payloadchar]") herald::datatype::Date now; herald::datatype::Date createdAt = now - herald::datatype::TimeInterval::minutes(1); // forces updated times to be greater than zero - std::shared_ptr delegate = std::make_shared(); + DummyBLEDeviceDelegate delegate; std::shared_ptr device = std::make_shared(id,delegate,createdAt); @@ -343,7 +343,7 @@ TEST_CASE("ble-device-update-payloadchar", "[ble][device][update][payloadchar]") REQUIRE(device->payloadCharacteristic().value() == uuid); // delegates - REQUIRE(!delegate->callbackCalled); + REQUIRE(!delegate.callbackCalled); herald::datatype::TimeInterval lu = device->timeIntervalSinceLastUpdate(); REQUIRE(lu == herald::datatype::TimeInterval::zero()); @@ -358,7 +358,7 @@ TEST_CASE("ble-device-update-signalchar", "[ble][device][update][signalchar]") { herald::datatype::Date now; herald::datatype::Date createdAt = now - herald::datatype::TimeInterval::minutes(1); // forces updated times to be greater than zero - std::shared_ptr delegate = std::make_shared(); + DummyBLEDeviceDelegate delegate; std::shared_ptr device = std::make_shared(id,delegate,createdAt); @@ -372,7 +372,7 @@ TEST_CASE("ble-device-update-signalchar", "[ble][device][update][signalchar]") { REQUIRE(device->signalCharacteristic().value() == uuid); // delegates - REQUIRE(!delegate->callbackCalled); + REQUIRE(!delegate.callbackCalled); herald::datatype::TimeInterval lu = device->timeIntervalSinceLastUpdate(); REQUIRE(lu == herald::datatype::TimeInterval::zero()); @@ -387,7 +387,7 @@ TEST_CASE("ble-device-invalidate-chars", "[ble][device][update][invalidatechars] herald::datatype::Date now; herald::datatype::Date createdAt = now - herald::datatype::TimeInterval::minutes(1); // forces updated times to be greater than zero - std::shared_ptr delegate = std::make_shared(); + DummyBLEDeviceDelegate delegate; std::shared_ptr device = std::make_shared(id,delegate,createdAt); @@ -412,7 +412,7 @@ TEST_CASE("ble-device-invalidate-chars", "[ble][device][update][invalidatechars] REQUIRE(device->payloadCharacteristic().has_value() == false); // delegates - REQUIRE(!delegate->callbackCalled); + REQUIRE(!delegate.callbackCalled); herald::datatype::TimeInterval lu = device->timeIntervalSinceLastUpdate(); REQUIRE(lu == herald::datatype::TimeInterval::zero()); @@ -429,7 +429,7 @@ TEST_CASE("ble-device-register-rssi", "[ble][device][register][rssi]") { herald::datatype::Date now; herald::datatype::Date createdAt = now - herald::datatype::TimeInterval::minutes(1); // forces updated times to be greater than zero - std::shared_ptr delegate = std::make_shared(); + DummyBLEDeviceDelegate delegate; std::shared_ptr device = std::make_shared(id,delegate,createdAt); @@ -442,7 +442,7 @@ TEST_CASE("ble-device-register-rssi", "[ble][device][register][rssi]") { REQUIRE(device->timeIntervalSinceLastWriteRssi() < herald::datatype::TimeInterval::never()); // delegates - REQUIRE(!delegate->callbackCalled); + REQUIRE(!delegate.callbackCalled); herald::datatype::TimeInterval lu = device->timeIntervalSinceLastUpdate(); REQUIRE(lu >= herald::datatype::TimeInterval::seconds(0)); diff --git a/herald-tests/coordinator-tests.cpp b/herald-tests/coordinator-tests.cpp index 10008ee..4c40a29 100644 --- a/herald-tests/coordinator-tests.cpp +++ b/herald-tests/coordinator-tests.cpp @@ -18,10 +18,10 @@ * to test the iteration functionality of the core Coordinator class */ -template +template class MockSensor : public herald::ble::Sensor { public: - MockSensor(std::shared_ptr> provider) : cp(provider) {} + MockSensor(CoordProvT& provider) : cp(provider) {} ~MockSensor() = default; @@ -30,17 +30,17 @@ class MockSensor : public herald::ble::Sensor { void stop() override {} /** For complex sensor coordination support, if required - Since v1.2-beta3 **/ - std::optional> coordinationProvider() override { - return std::optional>(cp); + std::optional> coordinationProvider() override { + return std::optional>(cp); } - std::shared_ptr> cp; + CoordProvT& cp; }; -template +template class NoOpHeraldV1ProtocolProvider : public herald::ble::HeraldProtocolV1Provider { public: - NoOpHeraldV1ProtocolProvider(ContextT& context,std::shared_ptr bledb) + NoOpHeraldV1ProtocolProvider(ContextT& context,BLEDBT& bledb) : ctx(context) HLOGGERINIT(ctx,"TESTS","NoOpProvider") {} @@ -111,10 +111,10 @@ class NoOpHeraldV1ProtocolProvider : public herald::ble::HeraldProtocolV1Provide HLOGGER(ContextT); }; -template +template class MockHeraldV1ProtocolProvider : public herald::ble::HeraldProtocolV1Provider { public: - MockHeraldV1ProtocolProvider(ContextT& context,std::shared_ptr bledb) + MockHeraldV1ProtocolProvider(ContextT& context,BLEDBT& bledb) : ctx(context), db(bledb), hasIdentifiedOs(false), lastDeviceOS(), hasReadPayload(false), lastDevicePayload(), hasImmediateSend(false), lastImmediateSend(), hasImmediateSendAll(false), lastImmediateSendAll() HLOGGERINIT(ctx,"TESTS","MockHeraldV1ProtocolProvider") @@ -179,7 +179,7 @@ class MockHeraldV1ProtocolProvider : public herald::ble::HeraldProtocolV1Provide std::optional serviceDiscovery(herald::engine::Activity act) override { HTDBG("serviceDiscovery called"); - auto device = db->device(std::get<1>(act.prerequisites.front()).value()); + auto device = db.device(std::get<1>(act.prerequisites.front()).value()); std::vector heraldServiceList; heraldServiceList.push_back(herald::ble::BLESensorConfiguration::serviceUUID); device->services(heraldServiceList); @@ -191,7 +191,7 @@ class MockHeraldV1ProtocolProvider : public herald::ble::HeraldProtocolV1Provide std::optional readPayload(herald::engine::Activity act) override { HTDBG("readPayload called"); - auto device = db->device(std::get<1>(act.prerequisites.front()).value()); + auto device = db.device(std::get<1>(act.prerequisites.front()).value()); device->payloadData(herald::datatype::Data(std::byte(0x02),2)); hasReadPayload = true; lastDevicePayload = device->identifier(); @@ -200,7 +200,7 @@ class MockHeraldV1ProtocolProvider : public herald::ble::HeraldProtocolV1Provide std::optional immediateSend(herald::engine::Activity act) override { HTDBG("immediateSend called"); - auto device = db->device(std::get<1>(act.prerequisites.front()).value()); + auto device = db.device(std::get<1>(act.prerequisites.front()).value()); hasImmediateSend = true; lastImmediateSend = device->identifier(); device->clearImmediateSendData(); @@ -209,7 +209,7 @@ class MockHeraldV1ProtocolProvider : public herald::ble::HeraldProtocolV1Provide std::optional immediateSendAll(herald::engine::Activity act) override { HTDBG("immediateSendAll called"); - auto device = db->device(std::get<1>(act.prerequisites.front()).value()); + auto device = db.device(std::get<1>(act.prerequisites.front()).value()); hasImmediateSendAll = true; lastImmediateSendAll = device->identifier(); device->clearImmediateSendData(); @@ -217,7 +217,7 @@ class MockHeraldV1ProtocolProvider : public herald::ble::HeraldProtocolV1Provide } ContextT& ctx; - std::shared_ptr db; + BLEDBT& db; bool hasIdentifiedOs; std::optional lastDeviceOS; @@ -243,21 +243,22 @@ TEST_CASE("coordinator-complex-iterations", "[coordinator][iterations][complex]" DummyBluetoothStateManager dbsm; herald::Context ctx(dls,dbsm); // default context include using CT = typename herald::Context; - std::shared_ptr> db = - std::make_shared>(ctx); - std::shared_ptr> pp = - std::make_shared>(ctx,db); - std::shared_ptr> coord = - std::make_shared>(ctx,db,pp); + herald::ble::ConcreteBLEDatabase db(ctx); + MockHeraldV1ProtocolProvider pp(ctx,db); + herald::ble::HeraldProtocolBLECoordinationProvider coord(ctx,db,pp); + using CPT = herald::ble::HeraldProtocolBLECoordinationProvider< + CT, + herald::ble::ConcreteBLEDatabase, + MockHeraldV1ProtocolProvider> + >; // Mock Sensor - std::shared_ptr> mockSensor = std::make_shared>(coord); + std::shared_ptr> mockSensor = std::make_shared>(coord); // register ble coordinator - std::shared_ptr> c = - std::make_shared>(ctx); - c->add(mockSensor); // registers the BLE coordinator - c->start(); + herald::engine::Coordinator c(ctx); + c.add(mockSensor); // registers the BLE coordinator + c.start(); // section wide data definitions herald::datatype::Data devMac1(std::byte(0x1d),6); @@ -272,31 +273,31 @@ TEST_CASE("coordinator-complex-iterations", "[coordinator][iterations][complex]" // std::shared_ptr devPtr3 = db->device(device3); SECTION("blecoordinator-complex-iterations-01-device1") { - std::shared_ptr devPtr1 = db->device(device1); + std::shared_ptr devPtr1 = db.device(device1); // std::vector>> conns = // coord->requiredConnections(); // Now perform one iteration - c->iteration(); + c.iteration(); // check provider used - REQUIRE(pp->hasIdentifiedOs == true); - REQUIRE(pp->lastDeviceOS == device1); - REQUIRE(pp->hasReadPayload == false); - REQUIRE(pp->hasImmediateSend == false); - REQUIRE(pp->hasImmediateSendAll == false); + REQUIRE(pp.hasIdentifiedOs == true); + REQUIRE(pp.lastDeviceOS == device1); + REQUIRE(pp.hasReadPayload == false); + REQUIRE(pp.hasImmediateSend == false); + REQUIRE(pp.hasImmediateSendAll == false); //} // second iteration should read payload - c->iteration(); - REQUIRE(pp->hasIdentifiedOs == true); - REQUIRE(pp->lastDeviceOS == device1); - REQUIRE(pp->hasReadPayload == true); - REQUIRE(pp->lastDevicePayload == device1); - REQUIRE(pp->hasImmediateSend == false); - REQUIRE(pp->hasImmediateSendAll == false); + c.iteration(); + REQUIRE(pp.hasIdentifiedOs == true); + REQUIRE(pp.lastDeviceOS == device1); + REQUIRE(pp.hasReadPayload == true); + REQUIRE(pp.lastDevicePayload == device1); + REQUIRE(pp.hasImmediateSend == false); + REQUIRE(pp.hasImmediateSendAll == false); //SECTION("blecoordinator-complex-iterations-02-immediatesend-device1") { //std::shared_ptr devPtr1 = db->device(device1); @@ -309,33 +310,33 @@ TEST_CASE("coordinator-complex-iterations", "[coordinator][iterations][complex]" devPtr1->immediateSendData(herald::datatype::Data(std::byte(0x09),4)); // Now perform one iteration - c->iteration(); + c.iteration(); // check provider used - REQUIRE(pp->hasIdentifiedOs == true); - REQUIRE(pp->lastDeviceOS == device1); - REQUIRE(pp->hasReadPayload == true); - REQUIRE(pp->lastDevicePayload == device1); - REQUIRE(pp->hasImmediateSend == true); - REQUIRE(pp->lastImmediateSend == device1); - REQUIRE(pp->hasImmediateSendAll == false); + REQUIRE(pp.hasIdentifiedOs == true); + REQUIRE(pp.lastDeviceOS == device1); + REQUIRE(pp.hasReadPayload == true); + REQUIRE(pp.lastDevicePayload == device1); + REQUIRE(pp.hasImmediateSend == true); + REQUIRE(pp.lastImmediateSend == device1); + REQUIRE(pp.hasImmediateSendAll == false); //} //SECTION("blecoordinator-complex-iterations-03-noactivity") { // No changes to required activity // Now perform one iteration - c->iteration(); + c.iteration(); // check provider used - REQUIRE(pp->hasIdentifiedOs == true); - REQUIRE(pp->lastDeviceOS == device1); - REQUIRE(pp->hasReadPayload == true); - REQUIRE(pp->lastDevicePayload == device1); - REQUIRE(pp->hasImmediateSend == true); - REQUIRE(pp->lastImmediateSend == device1); - REQUIRE(pp->hasImmediateSendAll == false); + REQUIRE(pp.hasIdentifiedOs == true); + REQUIRE(pp.lastDeviceOS == device1); + REQUIRE(pp.hasReadPayload == true); + REQUIRE(pp.lastDevicePayload == device1); + REQUIRE(pp.hasImmediateSend == true); + REQUIRE(pp.lastImmediateSend == device1); + REQUIRE(pp.hasImmediateSendAll == false); } - c->stop(); + c.stop(); } diff --git a/herald-wearable/src/main.cpp b/herald-wearable/src/main.cpp index 8a7fd6d..36fbb20 100644 --- a/herald-wearable/src/main.cpp +++ b/herald-wearable/src/main.cpp @@ -289,7 +289,7 @@ void herald_entry() { // Create Herald sensor array - this handles both advertising (Transmitter) and scanning/connecting (Receiver) SensorArray sa(ctx,pds); - ConcreteBLESensor,ConcreteBLEReceiver> ble(ctx); + ConcreteBLESensor,ConcreteBLEReceiver> ble(ctx,ctx.getBluetoothStateManager(),pds); // Add contacts.log delegate // CURRENTLY BREAKS ZEPHYR - DON'T KNOW WHY YET - LOGGING SUBSYSTEM ISSUE diff --git a/herald/include/herald/ble/ble_concrete.h b/herald/include/herald/ble/ble_concrete.h index 7382f96..cbde122 100644 --- a/herald/include/herald/ble/ble_concrete.h +++ b/herald/include/herald/ble/ble_concrete.h @@ -66,7 +66,7 @@ class ConcreteBLESensor : public BLESensor, public BLEDatabaseDelegate, transmitter(ctx, bluetoothStateManager, payloadDataSupplier, database), receiver(ctx, bluetoothStateManager, payloadDataSupplier, database), delegates(), - coordinator(std::make_shared>(ctx, database, receiver)), + coordinator(ctx, database, receiver), addedSelfAsDelegate(false) HLOGGERINIT(ctx,"sensor","ConcreteBLESensor") { @@ -77,11 +77,12 @@ class ConcreteBLESensor : public BLESensor, public BLEDatabaseDelegate, ~ConcreteBLESensor() = default; // Coordination overrides - Since v1.2-beta3 - std::optional> coordinationProvider() override { + std::optional> coordinationProvider() override { // Only return this if we support scanning if (BLESensorConfiguration::scanningEnabled) { HTDBG("Providing a BLECoordinationProvider"); - return std::optional>(coordinator); + //return std::optional>(std::static_cast(coordinator)); + return coordinator; } HTDBG("Scanning not supported - so not returning a BLECoordinationProvider"); return {}; @@ -222,7 +223,7 @@ class ConcreteBLESensor : public BLESensor, public BLEDatabaseDelegate, std::vector> delegates; - std::shared_ptr> coordinator; + HeraldProtocolBLECoordinationProvider,ReceiverT> coordinator; bool addedSelfAsDelegate; diff --git a/herald/include/herald/ble/ble_concrete_database.h b/herald/include/herald/ble/ble_concrete_database.h index e7a0401..30b9fef 100644 --- a/herald/include/herald/ble/ble_concrete_database.h +++ b/herald/include/herald/ble/ble_concrete_database.h @@ -42,7 +42,7 @@ struct last_updated_descending { }; template -class ConcreteBLEDatabase : public BLEDatabase, public BLEDeviceDelegate, public std::enable_shared_from_this> { +class ConcreteBLEDatabase : public BLEDatabase, public BLEDeviceDelegate /*, public std::enable_shared_from_this>*/ { public: ConcreteBLEDatabase(ContextT& context) : ctx(context), @@ -179,7 +179,7 @@ class ConcreteBLEDatabase : public BLEDatabase, public BLEDeviceDelegate, public return results.front(); // TODO ensure we send back the latest, not just the first match } std::shared_ptr newDevice = std::make_shared( - TargetIdentifier(payloadData), this->shared_from_this()); + TargetIdentifier(payloadData), *this); //this->shared_from_this()); devices.push_back(newDevice); for (auto delegate : delegates) { delegate->bleDatabaseDidCreate(newDevice); @@ -195,7 +195,7 @@ class ConcreteBLEDatabase : public BLEDatabase, public BLEDeviceDelegate, public } HTDBG("New target identified: {}",(std::string)targetIdentifier); std::shared_ptr newDevice = std::make_shared( - targetIdentifier, this->shared_from_this()); + targetIdentifier, *this);// this->shared_from_this()); devices.push_back(newDevice); for (auto delegate : delegates) { delegate->bleDatabaseDidCreate(newDevice); diff --git a/herald/include/herald/ble/ble_coordinator.h b/herald/include/herald/ble/ble_coordinator.h index 4218c51..2ad8410 100644 --- a/herald/include/herald/ble/ble_coordinator.h +++ b/herald/include/herald/ble/ble_coordinator.h @@ -23,11 +23,11 @@ namespace herald { namespace ble { -template +template class HeraldProtocolBLECoordinationProvider : public CoordinationProvider { public: - HeraldProtocolBLECoordinationProvider(ContextT& ctx, std::shared_ptr bledb, - std::shared_ptr provider) + HeraldProtocolBLECoordinationProvider(ContextT& ctx, BLEDBT& bledb, + ProviderT& provider) : context(ctx), db(bledb), pp(provider), @@ -69,7 +69,7 @@ class HeraldProtocolBLECoordinationProvider : public CoordinationProvider { // We can't disconnect from 'all', so check for target if (optTarget.has_value()) { // Ignoring closed true/false result - pp->closeConnection(optTarget.value()); + pp.closeConnection(optTarget.value()); } } } @@ -99,7 +99,7 @@ class HeraldProtocolBLECoordinationProvider : public CoordinationProvider { // }); // fut.get(); // TODO FIND OUT HOW TO DO THIS FUTURE WAITING FUNCTIONALITY - lastConnectionSuccessful = pp->openConnection(optTarget.value()); + lastConnectionSuccessful = pp.openConnection(optTarget.value()); // If successful, add to provisioned list if (lastConnectionSuccessful) { @@ -143,7 +143,7 @@ class HeraldProtocolBLECoordinationProvider : public CoordinationProvider { iterationsSinceBreak < (breakEvery + breakFor) ) { HTDBG("###### Skipping connections - giving advertising & scanning a chance"); // if (iterationsSinceBreak == breakEvery) { // incase it fails - pp->restartScanningAndAdvertising(); + pp.restartScanningAndAdvertising(); // } return results; } else if (iterationsSinceBreak == (breakEvery + breakFor) ) { @@ -152,7 +152,7 @@ class HeraldProtocolBLECoordinationProvider : public CoordinationProvider { } // Remove expired devices - auto expired = db->matches([/*this*/] (const std::shared_ptr& device) -> bool { + auto expired = db.matches([/*this*/] (const std::shared_ptr& device) -> bool { auto interval = device->timeIntervalSinceLastUpdate(); bool notZero = interval != TimeInterval::zero(); bool isOld = interval > TimeInterval::minutes(15); @@ -166,7 +166,7 @@ class HeraldProtocolBLECoordinationProvider : public CoordinationProvider { return notZero && isOld; }); for (auto& exp : expired) { - db->remove(exp->identifier()); + db.remove(exp->identifier()); HTDBG("Removing expired device with ID: "); HTDBG((std::string)exp->identifier()); HTDBG("time since last update:-"); @@ -174,7 +174,7 @@ class HeraldProtocolBLECoordinationProvider : public CoordinationProvider { } // Allow updates from ignored (for a time) status, to retry status - auto tempIgnoredOS = db->matches([](const std::shared_ptr& device) -> bool { + auto tempIgnoredOS = db.matches([](const std::shared_ptr& device) -> bool { return device->operatingSystem() == BLEDeviceOperatingSystem::ignore; }); for (auto& device : tempIgnoredOS) { @@ -184,7 +184,7 @@ class HeraldProtocolBLECoordinationProvider : public CoordinationProvider { // Add all targets in database that are not known - auto newConns = db->matches([](const std::shared_ptr& device) -> bool { + auto newConns = db.matches([](const std::shared_ptr& device) -> bool { return !device->ignore() && ( !device->hasService(BLESensorConfiguration::serviceUUID) @@ -208,7 +208,7 @@ class HeraldProtocolBLECoordinationProvider : public CoordinationProvider { if (newConns.size() > 0) { // print debug info about the BLE Database HTDBG("BLE DATABASE CURRENT CONTENTS:-"); - auto allDevices = db->matches([](const std::shared_ptr& device) -> bool { + auto allDevices = db.matches([](const std::shared_ptr& device) -> bool { return true; }); for (auto& device : allDevices) { @@ -262,7 +262,7 @@ class HeraldProtocolBLECoordinationProvider : public CoordinationProvider { } } else { // restart scanning when no connection activity is expected - pp->restartScanningAndAdvertising(); + pp.restartScanningAndAdvertising(); } return results; @@ -283,21 +283,21 @@ class HeraldProtocolBLECoordinationProvider : public CoordinationProvider { // TODO is IOS and needs payload sharing - // auto state0Devices = db->matches([](std::shared_ptr device) -> bool { + // auto state0Devices = db.matches([](std::shared_ptr device) -> bool { // return !device->ignore() && !device->pseudoDeviceAddress().has_value(); // }); - auto state1Devices = db->matches([](const std::shared_ptr& device) -> bool { + auto state1Devices = db.matches([](const std::shared_ptr& device) -> bool { return !device->ignore() && !device->receiveOnly() && !device->hasService(BLESensorConfiguration::serviceUUID); }); - auto state2Devices = db->matches([](const std::shared_ptr& device) -> bool { + auto state2Devices = db.matches([](const std::shared_ptr& device) -> bool { return !device->ignore() && !device->receiveOnly() && device->hasService(BLESensorConfiguration::serviceUUID) && !device->payloadData().has_value(); // TODO check for Herald transferred payload data (not legacy) }); - auto state4Devices = db->matches([](const std::shared_ptr& device) -> bool { + auto state4Devices = db.matches([](const std::shared_ptr& device) -> bool { return !device->ignore() && !device->receiveOnly() && device->hasService(BLESensorConfiguration::serviceUUID) && @@ -325,7 +325,7 @@ class HeraldProtocolBLECoordinationProvider : public CoordinationProvider { // } .executor = [this](const Activity activity) -> std::optional { // fill this out - pp->serviceDiscovery(activity); + pp.serviceDiscovery(activity); return {}; } }); @@ -349,7 +349,7 @@ class HeraldProtocolBLECoordinationProvider : public CoordinationProvider { // } .executor = [this](const Activity activity) -> std::optional { // fill this out - pp->readPayload(activity); + pp.readPayload(activity); return {}; } }); @@ -377,7 +377,7 @@ class HeraldProtocolBLECoordinationProvider : public CoordinationProvider { // } .executor = [this](const Activity activity) -> std::optional { // fill this out - pp->immediateSend(activity); + pp.immediateSend(activity); return {}; } }); @@ -389,8 +389,8 @@ class HeraldProtocolBLECoordinationProvider : public CoordinationProvider { private: ContextT& context; - std::shared_ptr db; - std::shared_ptr pp; + BLEDBT& db; + ProviderT& pp; std::vector previouslyProvisioned; diff --git a/herald/include/herald/ble/ble_device.h b/herald/include/herald/ble/ble_device.h index 50debcd..98395d8 100644 --- a/herald/include/herald/ble/ble_device.h +++ b/herald/include/herald/ble/ble_device.h @@ -45,7 +45,7 @@ enum class BLEDeviceState : int { class BLEDevice : public Device, public std::enable_shared_from_this { public: - BLEDevice(TargetIdentifier identifier, std::shared_ptr delegate, const Date& created = Date()); + BLEDevice(TargetIdentifier identifier, BLEDeviceDelegate& delegate, const Date& created = Date()); BLEDevice(const BLEDevice& other); // copy ctor BLEDevice(BLEDevice&& other) = delete; // remove move constructor ~BLEDevice(); diff --git a/herald/include/herald/engine/coordinator.h b/herald/include/herald/engine/coordinator.h index 1497550..fc15525 100644 --- a/herald/include/herald/engine/coordinator.h +++ b/herald/include/herald/engine/coordinator.h @@ -72,7 +72,7 @@ class Coordinator { featureProviders.clear(); // Fetch feature providers for (auto prov: providers) { - auto myFeatures = prov->connectionsProvided(); + auto myFeatures = prov.get().connectionsProvided(); for (auto feature : myFeatures) { featureProviders.emplace(feature,prov); } @@ -90,7 +90,7 @@ class Coordinator { HTDBG("################# ITERATION #################"); // HTDBG("Entered iteration"); // Create empty list of required prereqs per provider - std::map,std::vector> assignPrereqs; + std::map,std::vector> assignPrereqs; for (auto& prov : providers) { assignPrereqs.emplace(prov,std::vector()); } @@ -100,7 +100,7 @@ class Coordinator { std::vector connsRequired; // Loop over providers and ask for feature pre-requisites for (auto& prov : providers) { - auto myConns = prov->requiredConnections(); + auto myConns = prov.get().requiredConnections(); std::copy(myConns.begin(),myConns.end(), std::back_insert_iterator>(connsRequired)); } @@ -141,7 +141,7 @@ class Coordinator { // fut.get(); // waits for callback // TODO wait with timeout // FOR OTHER PLATFORMS (E.g. ZEPHYR):- - std::vector myProvisioned = prov.first->provision(prov.second); + std::vector myProvisioned = prov.first.get().provision(prov.second); std::copy(myProvisioned.begin(),myProvisioned.end(), std::back_insert_iterator>(provisioned)); } @@ -150,7 +150,7 @@ class Coordinator { // For each which are now present, ask for activities (in descending priority order) for (auto& prov : providers) { - auto maxActs = prov->requiredActivities(); + auto maxActs = prov.get().requiredActivities(); // TODO sort by descending priority before actioning for (auto& act : maxActs) { std::string san("Activity "); @@ -203,16 +203,16 @@ class Coordinator { private: ContextT& context; - std::vector> providers; - std::map> featureProviders; + std::vector> providers; + std::map> featureProviders; bool running; HLOGGER(ContextT); }; -/** Comparator for less than (use in maps) **/ -bool operator<(const std::shared_ptr& first, const std::shared_ptr& second); +// /** Comparator for less than (use in maps) **/ +bool operator<(const std::reference_wrapper first, const std::reference_wrapper second); } } diff --git a/herald/include/herald/sensor.h b/herald/include/herald/sensor.h index 74503af..65eb4b3 100644 --- a/herald/include/herald/sensor.h +++ b/herald/include/herald/sensor.h @@ -26,7 +26,7 @@ class Sensor { virtual void stop() = 0; /// \brief For complex sensor coordination support, if required - Since v1.2-beta3 - virtual std::optional> coordinationProvider() = 0; + virtual std::optional> coordinationProvider() = 0; }; diff --git a/herald/include/herald/sensor_array.h b/herald/include/herald/sensor_array.h index 8d91411..73ea84d 100644 --- a/herald/include/herald/sensor_array.h +++ b/herald/include/herald/sensor_array.h @@ -97,7 +97,7 @@ class SensorArray : public Sensor { } } - std::optional> coordinationProvider() override { + std::optional> coordinationProvider() override { return {}; } diff --git a/herald/src/ble/ble_device.cpp b/herald/src/ble/ble_device.cpp index c6824a9..89a5a5d 100644 --- a/herald/src/ble/ble_device.cpp +++ b/herald/src/ble/ble_device.cpp @@ -23,7 +23,7 @@ using namespace herald::ble::filter; class BLEDevice::Impl { public: - Impl(TargetIdentifier identifier, std::shared_ptr del, const Date& createdAt); + Impl(TargetIdentifier identifier, BLEDeviceDelegate& del, const Date& createdAt); Impl(const Impl& other); // copy ctor Impl(Impl&& other) = delete; ~Impl(); @@ -32,7 +32,7 @@ class BLEDevice::Impl { Impl operator=(Impl&& other) = delete; TargetIdentifier id; - std::shared_ptr delegate; + BLEDeviceDelegate& delegate; // Data holders Date created; @@ -67,7 +67,7 @@ class BLEDevice::Impl { int connectRepeatedFailures; }; -BLEDevice::Impl::Impl(TargetIdentifier identifier, std::shared_ptr del, const Date& createdAt) +BLEDevice::Impl::Impl(TargetIdentifier identifier, BLEDeviceDelegate& del, const Date& createdAt) : id(identifier), delegate(del), created(createdAt), @@ -170,7 +170,7 @@ BLEDevice::Impl::operator=(const Impl& other) -BLEDevice::BLEDevice(TargetIdentifier identifier, std::shared_ptr delegate, +BLEDevice::BLEDevice(TargetIdentifier identifier, BLEDeviceDelegate& delegate, const Date& createdAt) : Device(), mImpl(std::make_unique(identifier,delegate,createdAt)) @@ -340,7 +340,7 @@ BLEDevice::state(BLEDeviceState newState) if (changed) { mImpl->state.emplace(newState); mImpl->lastUpdated.emplace(); // Constructs Date as now - mImpl->delegate->device(shared_from_this(), BLEDeviceAttribute::state); + mImpl->delegate.device(shared_from_this(), BLEDeviceAttribute::state); } } @@ -377,7 +377,7 @@ BLEDevice::operatingSystem(BLEDeviceOperatingSystem newOS) bool changed = !mImpl->os.has_value() || mImpl->os.value() != newOS; if (changed) { mImpl->os.emplace(newOS); - mImpl->delegate->device(shared_from_this(), BLEDeviceAttribute::operatingSystem); + mImpl->delegate.device(shared_from_this(), BLEDeviceAttribute::operatingSystem); } } @@ -395,7 +395,7 @@ BLEDevice::payloadData(PayloadData newPayloadData) mImpl->payload.emplace(newPayloadData); mImpl->lastUpdated.emplace(); // Constructs Date as now mImpl->payloadUpdated = Date(); - mImpl->delegate->device(shared_from_this(), BLEDeviceAttribute::payloadData); + mImpl->delegate.device(shared_from_this(), BLEDeviceAttribute::payloadData); } } @@ -412,7 +412,7 @@ BLEDevice::immediateSendData(ImmediateSendData toSend) if (changed) { mImpl->immediateSendData.emplace(toSend); mImpl->lastUpdated.emplace(); // Constructs Date as now - mImpl->delegate->device(shared_from_this(), BLEDeviceAttribute::immediateSendData); + mImpl->delegate.device(shared_from_this(), BLEDeviceAttribute::immediateSendData); } } @@ -435,7 +435,7 @@ BLEDevice::rssi(RSSI newRSSI) if (changed) { mImpl->rssi.emplace(newRSSI); mImpl->lastUpdated.emplace(); // Constructs Date as now - mImpl->delegate->device(shared_from_this(), BLEDeviceAttribute::rssi); + mImpl->delegate.device(shared_from_this(), BLEDeviceAttribute::rssi); } } @@ -452,7 +452,7 @@ BLEDevice::txPower(BLETxPower newPower) if (changed) { mImpl->txPower.emplace(newPower); mImpl->lastUpdated.emplace(); // Constructs Date as now - mImpl->delegate->device(shared_from_this(), BLEDeviceAttribute::txPower); + mImpl->delegate.device(shared_from_this(), BLEDeviceAttribute::txPower); } } diff --git a/herald/src/ble/zephyr/concrete_ble_receiver.cpp b/herald/src/ble/zephyr/concrete_ble_receiver.cpp index db0d4c8..c0049ac 100644 --- a/herald/src/ble/zephyr/concrete_ble_receiver.cpp +++ b/herald/src/ble/zephyr/concrete_ble_receiver.cpp @@ -4,6 +4,7 @@ #include "herald/zephyr_context.h" #include "herald/data/sensor_logger.h" +#include "herald/ble/zephyr/concrete_ble_receiver.h" #include "herald/ble/ble_concrete.h" #include "herald/ble/ble_database.h" #include "herald/ble/ble_receiver.h" diff --git a/herald/src/ble/zephyr/concrete_ble_transmitter.cpp b/herald/src/ble/zephyr/concrete_ble_transmitter.cpp index ffd52a8..7f04f92 100644 --- a/herald/src/ble/zephyr/concrete_ble_transmitter.cpp +++ b/herald/src/ble/zephyr/concrete_ble_transmitter.cpp @@ -4,6 +4,7 @@ #include "herald/zephyr_context.h" #include "herald/ble/ble_concrete.h" +#include "herald/ble/zephyr/concrete_ble_transmitter.h" #include "herald/ble/ble_database.h" #include "herald/ble/ble_receiver.h" #include "herald/ble/ble_sensor.h" diff --git a/herald/src/engine/coordinator.cpp b/herald/src/engine/coordinator.cpp index 26f2bc2..ea1eb74 100644 --- a/herald/src/engine/coordinator.cpp +++ b/herald/src/engine/coordinator.cpp @@ -9,9 +9,9 @@ namespace herald { namespace engine { -bool operator<(const std::shared_ptr& first, const std::shared_ptr& second) +bool operator<(const std::reference_wrapper first, const std::reference_wrapper second) { - return &(*first) < &(*second); // simple memory address comparator of item pointed TO + return &(first.get()) < &(second.get()); // use reference_wrapper's call operator to fetch the underlying reference } } From ad262c94a9e8fef6a1083c4ca113ce435f7c2be5 Mon Sep 17 00:00:00 2001 From: Adam Fowler Date: Sat, 10 Apr 2021 11:54:12 +0100 Subject: [PATCH 21/28] Zephyr tx and rx moved to templates Known issues:- - Some static callback functions defined in headers instead of implementation files Signed-off-by: Adam Fowler --- herald-tests/analysissensor-tests.cpp | 5 +- herald-tests/blecoordinator-tests.cpp | 40 +- herald-tests/bledatabase-tests.cpp | 69 +- herald-tests/coordinator-tests.cpp | 5 +- herald-tests/errorcontactlog-tests.cpp | 7 +- herald-tests/sensorlogger-tests.cpp | 40 +- herald-tests/simplepayload-tests.cpp | 10 +- herald-tests/test-templates.h | 2 +- herald-wearable/.vscode/launch.json | 3 +- herald-wearable/.vscode/settings.json | 3 + herald-wearable/src/main.cpp | 41 +- .../analysis/logging_analysis_delegate.h | 10 +- herald/include/herald/ble/ble_concrete.h | 30 +- .../herald/ble/ble_concrete_database.h | 22 +- herald/include/herald/ble/ble_database.h | 2 +- .../herald/ble/bluetooth_state_manager.h | 2 +- .../ble/default/concrete_ble_receiver.h | 54 + .../ble/default/concrete_ble_transmitter.h | 68 + .../herald/ble/zephyr/concrete_ble_receiver.h | 1058 +++++++++++++- .../ble/zephyr/concrete_ble_transmitter.h | 223 ++- herald/include/herald/context.h | 21 +- .../herald/data/devnull_logging_sink.h | 21 + herald/include/herald/zephyr_context.h | 8 +- .../src/ble/zephyr/concrete_ble_receiver.cpp | 1224 ----------------- .../ble/zephyr/concrete_ble_transmitter.cpp | 364 +---- herald/src/data/devnull_logging_sink.cpp | 21 + herald/src/zephyr_context.cpp | 12 +- 27 files changed, 1673 insertions(+), 1692 deletions(-) create mode 100644 herald/include/herald/ble/default/concrete_ble_receiver.h create mode 100644 herald/include/herald/ble/default/concrete_ble_transmitter.h create mode 100644 herald/include/herald/data/devnull_logging_sink.h create mode 100644 herald/src/data/devnull_logging_sink.cpp diff --git a/herald-tests/analysissensor-tests.cpp b/herald-tests/analysissensor-tests.cpp index 84d686a..09bd5c4 100644 --- a/herald-tests/analysissensor-tests.cpp +++ b/herald-tests/analysissensor-tests.cpp @@ -110,8 +110,9 @@ TEST_CASE("analysissensor-output", "[sensorlogger][analysissensor][output]") { SECTION("analysissensor-output") { DummyLoggingSink dls; DummyBluetoothStateManager dbsm; - herald::Context ctx(dls,dbsm); // default context include - using CT = typename herald::Context; + herald::DefaultPlatformType dpt; + herald::Context ctx(dpt,dls,dbsm); // default context include + using CT = typename herald::Context; //herald::data::SensorLogger logger(ctx,"testout","analysissensor"); herald::analysis::LoggingAnalysisDelegate lad(ctx); // The subject of this test std::cout << "LoggingAnalysisDelegate::RAM = " << sizeof(lad) << std::endl; diff --git a/herald-tests/blecoordinator-tests.cpp b/herald-tests/blecoordinator-tests.cpp index fb467b0..ffe0d3a 100644 --- a/herald-tests/blecoordinator-tests.cpp +++ b/herald-tests/blecoordinator-tests.cpp @@ -117,8 +117,9 @@ TEST_CASE("blecoordinator-ctor", "[coordinator][ctor][basic]") { SECTION("blecoordinator-ctor") { DummyLoggingSink dls; DummyBluetoothStateManager dbsm; - herald::Context ctx(dls,dbsm); // default context include - using CT = typename herald::Context; + herald::DefaultPlatformType dpt; + herald::Context ctx(dpt,dls,dbsm); // default context include + using CT = typename herald::Context; herald::ble::ConcreteBLEDatabase db(ctx); NoOpHeraldV1ProtocolProvider pp(ctx,db); herald::ble::HeraldProtocolBLECoordinationProvider coord(ctx,db,pp); @@ -143,8 +144,9 @@ TEST_CASE("blecoordinator-unseen-device", "[coordinator][unseen-device][basic]") SECTION("blecoordinator-unseen-device") { DummyLoggingSink dls; DummyBluetoothStateManager dbsm; - herald::Context ctx(dls,dbsm); // default context include - using CT = typename herald::Context; + herald::DefaultPlatformType dpt; + herald::Context ctx(dpt,dls,dbsm); // default context include + using CT = typename herald::Context; herald::ble::ConcreteBLEDatabase db(ctx); NoOpHeraldV1ProtocolProvider pp(ctx,db); herald::ble::HeraldProtocolBLECoordinationProvider coord(ctx,db,pp); @@ -175,8 +177,9 @@ TEST_CASE("blecoordinator-android-no-id", "[coordinator][android-no-id][basic]") SECTION("blecoordinator-android-no-id") { DummyLoggingSink dls; DummyBluetoothStateManager dbsm; - herald::Context ctx(dls,dbsm); // default context include - using CT = typename herald::Context; + herald::DefaultPlatformType dpt; + herald::Context ctx(dpt,dls,dbsm); // default context include + using CT = typename herald::Context; herald::ble::ConcreteBLEDatabase db(ctx); NoOpHeraldV1ProtocolProvider pp(ctx,db); herald::ble::HeraldProtocolBLECoordinationProvider coord(ctx,db,pp); @@ -212,8 +215,9 @@ TEST_CASE("blecoordinator-two-mixed-no-id", "[coordinator][two-mixed-no-id][basi SECTION("blecoordinator-two-mixed-no-id") { DummyLoggingSink dls; DummyBluetoothStateManager dbsm; - herald::Context ctx(dls,dbsm); // default context include - using CT = typename herald::Context; + herald::DefaultPlatformType dpt; + herald::Context ctx(dpt,dls,dbsm); // default context include + using CT = typename herald::Context; herald::ble::ConcreteBLEDatabase db(ctx); NoOpHeraldV1ProtocolProvider pp(ctx,db); herald::ble::HeraldProtocolBLECoordinationProvider coord(ctx,db,pp); @@ -255,8 +259,9 @@ TEST_CASE("blecoordinator-got-os-and-id", "[coordinator][got-os-and-id][basic]") SECTION("blecoordinator-got-os-and-id") { DummyLoggingSink dls; DummyBluetoothStateManager dbsm; - herald::Context ctx(dls,dbsm); // default context include - using CT = typename herald::Context; + herald::DefaultPlatformType dpt; + herald::Context ctx(dpt,dls,dbsm); // default context include + using CT = typename herald::Context; herald::ble::ConcreteBLEDatabase db(ctx); NoOpHeraldV1ProtocolProvider pp(ctx,db); herald::ble::HeraldProtocolBLECoordinationProvider coord(ctx,db,pp); @@ -286,8 +291,9 @@ TEST_CASE("blecoordinator-got-two-at-different-states", "[coordinator][got-two-a SECTION("blecoordinator-got-two-at-different-states") { DummyLoggingSink dls; DummyBluetoothStateManager dbsm; - herald::Context ctx(dls,dbsm); // default context include - using CT = typename herald::Context; + herald::DefaultPlatformType dpt; + herald::Context ctx(dpt,dls,dbsm); // default context include + using CT = typename herald::Context; herald::ble::ConcreteBLEDatabase db(ctx); NoOpHeraldV1ProtocolProvider pp(ctx,db); herald::ble::HeraldProtocolBLECoordinationProvider coord(ctx,db,pp); @@ -329,8 +335,9 @@ TEST_CASE("blecoordinator-got-immediate-send-targeted", "[coordinator][got-immed SECTION("blecoordinator-got-immediate-send-targeted") { DummyLoggingSink dls; DummyBluetoothStateManager dbsm; - herald::Context ctx(dls,dbsm); // default context include - using CT = typename herald::Context; + herald::DefaultPlatformType dpt; + herald::Context ctx(dpt,dls,dbsm); // default context include + using CT = typename herald::Context; herald::ble::ConcreteBLEDatabase db(ctx); NoOpHeraldV1ProtocolProvider pp(ctx,db); herald::ble::HeraldProtocolBLECoordinationProvider coord(ctx,db,pp); @@ -362,8 +369,9 @@ TEST_CASE("blecoordinator-got-three-at-different-states", "[coordinator][got-thr SECTION("blecoordinator-got-three-at-different-states") { DummyLoggingSink dls; DummyBluetoothStateManager dbsm; - herald::Context ctx(dls,dbsm); // default context include - using CT = typename herald::Context; + herald::DefaultPlatformType dpt; + herald::Context ctx(dpt,dls,dbsm); // default context include + using CT = typename herald::Context; herald::ble::ConcreteBLEDatabase db(ctx); NoOpHeraldV1ProtocolProvider pp(ctx,db); herald::ble::HeraldProtocolBLECoordinationProvider coord(ctx,db,pp); diff --git a/herald-tests/bledatabase-tests.cpp b/herald-tests/bledatabase-tests.cpp index 539ba43..e6ef732 100644 --- a/herald-tests/bledatabase-tests.cpp +++ b/herald-tests/bledatabase-tests.cpp @@ -46,8 +46,9 @@ TEST_CASE("ble-database-empty", "[ble][database][ctor][empty]") { SECTION("ble-database-empty") { DummyLoggingSink dls; DummyBluetoothStateManager dbsm; - herald::Context ctx(dls,dbsm); // default context include - using CT = typename herald::Context; + herald::DefaultPlatformType dpt; + herald::Context ctx(dpt,dls,dbsm); // default context include + using CT = typename herald::Context; std::shared_ptr> db = std::make_shared>(ctx); // enables shared_from_this @@ -59,18 +60,18 @@ TEST_CASE("ble-database-callback-verify", "[ble][database][callback][verify]") { SECTION("ble-callback-verify") { DummyLoggingSink dls; DummyBluetoothStateManager dbsm; - herald::Context ctx(dls,dbsm); // default context include - using CT = typename herald::Context; + herald::DefaultPlatformType dpt; + herald::Context ctx(dpt,dls,dbsm); // default context include + using CT = typename herald::Context; std::shared_ptr> db = std::make_shared>(ctx); // enables shared_from_this - std::shared_ptr delegate = - std::make_shared(); + DummyBLEDBDelegate delegate; db->add(delegate); REQUIRE(db->size() == 0); - REQUIRE(delegate->createCallbackCalled == false); - REQUIRE(delegate->updateCallbackCalled == false); - REQUIRE(delegate->deleteCallbackCalled == false); + REQUIRE(delegate.createCallbackCalled == false); + REQUIRE(delegate.updateCallbackCalled == false); + REQUIRE(delegate.deleteCallbackCalled == false); herald::datatype::Data devMac(std::byte(0x02),6); herald::datatype::TargetIdentifier dev(devMac); @@ -78,39 +79,39 @@ TEST_CASE("ble-database-callback-verify", "[ble][database][callback][verify]") { // add in new device std::shared_ptr devPtr = db->device(dev); REQUIRE(db->size() == 1); - REQUIRE(delegate->createCallbackCalled == true); - REQUIRE(delegate->updateCallbackCalled == false); - REQUIRE(delegate->deleteCallbackCalled == false); - REQUIRE(delegate->dev.has_value()); - REQUIRE(delegate->dev.value() == devPtr); + REQUIRE(delegate.createCallbackCalled == true); + REQUIRE(delegate.updateCallbackCalled == false); + REQUIRE(delegate.deleteCallbackCalled == false); + REQUIRE(delegate.dev.has_value()); + REQUIRE(delegate.dev.value() == devPtr); // add in a second device via the payload, not target identifier herald::datatype::PayloadData payload(std::byte(0x1f),6); std::shared_ptr devPtr2 = db->device(payload); REQUIRE(db->size() == 2); - REQUIRE(delegate->createCallbackCalled == true); - REQUIRE(delegate->updateCallbackCalled == false); - REQUIRE(delegate->deleteCallbackCalled == false); - REQUIRE(delegate->dev.has_value()); - REQUIRE(delegate->dev.value() == devPtr2); + REQUIRE(delegate.createCallbackCalled == true); + REQUIRE(delegate.updateCallbackCalled == false); + REQUIRE(delegate.deleteCallbackCalled == false); + REQUIRE(delegate.dev.has_value()); + REQUIRE(delegate.dev.value() == devPtr2); // update a device attribute devPtr->rssi(herald::datatype::RSSI{14}); REQUIRE(db->size() == 2); - REQUIRE(delegate->createCallbackCalled == true); - REQUIRE(delegate->updateCallbackCalled == true); - REQUIRE(delegate->deleteCallbackCalled == false); - REQUIRE(delegate->dev.has_value()); - REQUIRE(delegate->dev.value() == devPtr); + REQUIRE(delegate.createCallbackCalled == true); + REQUIRE(delegate.updateCallbackCalled == true); + REQUIRE(delegate.deleteCallbackCalled == false); + REQUIRE(delegate.dev.has_value()); + REQUIRE(delegate.dev.value() == devPtr); // delete the device db->remove(dev); REQUIRE(db->size() == 1); - REQUIRE(delegate->createCallbackCalled == true); - REQUIRE(delegate->updateCallbackCalled == true); - REQUIRE(delegate->deleteCallbackCalled == true); - REQUIRE(delegate->dev.has_value()); - REQUIRE(delegate->dev.value() == devPtr); + REQUIRE(delegate.createCallbackCalled == true); + REQUIRE(delegate.updateCallbackCalled == true); + REQUIRE(delegate.deleteCallbackCalled == true); + REQUIRE(delegate.dev.has_value()); + REQUIRE(delegate.dev.value() == devPtr); // delete non existant @@ -121,11 +122,11 @@ TEST_CASE("ble-database-callback-verify", "[ble][database][callback][verify]") { db->remove(dev3); // nothing should have changed REQUIRE(db->size() == 1); - REQUIRE(delegate->createCallbackCalled == true); - REQUIRE(delegate->updateCallbackCalled == true); - REQUIRE(delegate->deleteCallbackCalled == true); - REQUIRE(delegate->dev.has_value()); - REQUIRE(delegate->dev.value() == devPtr); + REQUIRE(delegate.createCallbackCalled == true); + REQUIRE(delegate.updateCallbackCalled == true); + REQUIRE(delegate.deleteCallbackCalled == true); + REQUIRE(delegate.dev.has_value()); + REQUIRE(delegate.dev.value() == devPtr); } } \ No newline at end of file diff --git a/herald-tests/coordinator-tests.cpp b/herald-tests/coordinator-tests.cpp index 4c40a29..bf2488d 100644 --- a/herald-tests/coordinator-tests.cpp +++ b/herald-tests/coordinator-tests.cpp @@ -241,8 +241,9 @@ TEST_CASE("coordinator-complex-iterations", "[coordinator][iterations][complex]" // create our BLE coordinator DummyLoggingSink dls; DummyBluetoothStateManager dbsm; - herald::Context ctx(dls,dbsm); // default context include - using CT = typename herald::Context; + herald::DefaultPlatformType dpt; + herald::Context ctx(dpt,dls,dbsm); // default context include + using CT = typename herald::Context; herald::ble::ConcreteBLEDatabase db(ctx); MockHeraldV1ProtocolProvider pp(ctx,db); herald::ble::HeraldProtocolBLECoordinationProvider coord(ctx,db,pp); diff --git a/herald-tests/errorcontactlog-tests.cpp b/herald-tests/errorcontactlog-tests.cpp index 2a368b5..b031f56 100644 --- a/herald-tests/errorcontactlog-tests.cpp +++ b/herald-tests/errorcontactlog-tests.cpp @@ -57,9 +57,10 @@ TEST_CASE("errorcontactlogger-output-dbg", "[errorcontactlogger][output]") { SECTION("errorcontactlogger-output-dbg") { DummyLoggingSink dls; DummyBluetoothStateManager dbsm; - herald::Context ctx(dls,dbsm); // default context include - using CT = typename herald::Context; - + herald::DefaultPlatformType dpt; + herald::Context ctx(dpt,dls,dbsm); // default context include + using CT = typename herald::Context; + // Create contact logger std::shared_ptr pdf = std::make_shared(); diff --git a/herald-tests/sensorlogger-tests.cpp b/herald-tests/sensorlogger-tests.cpp index b485212..16b158b 100644 --- a/herald-tests/sensorlogger-tests.cpp +++ b/herald-tests/sensorlogger-tests.cpp @@ -14,8 +14,9 @@ TEST_CASE("sensorlogger-output-dbg", "[sensorlogger][output]") { SECTION("sensorlogger-output-dbg") { DummyLoggingSink dls; DummyBluetoothStateManager dbsm; - herald::Context ctx(dls,dbsm); // default context include - // using CT = typename herald::Context; + herald::DefaultPlatformType dpt; + herald::Context ctx(dpt,dls,dbsm); // default context include + // using CT = typename herald::Context; herald::data::SensorLogger logger(ctx.getLoggingSink(),"testout","mytest"); HTDBG("Simple string"); @@ -53,8 +54,9 @@ TEST_CASE("sensorlogger-output-log", "[sensorlogger][output]") { SECTION("sensorlogger-output-log") { DummyLoggingSink dls; DummyBluetoothStateManager dbsm; - herald::Context ctx(dls,dbsm); // default context include - // using CT = typename herald::Context; + herald::DefaultPlatformType dpt; + herald::Context ctx(dpt,dls,dbsm); // default context include + // using CT = typename herald::Context; herald::data::SensorLogger logger(ctx.getLoggingSink(),"testout","mytest"); HTLOG("Simple string"); @@ -87,8 +89,9 @@ TEST_CASE("sensorlogger-output-fault", "[sensorlogger][output]") { SECTION("sensorlogger-output-fault") { DummyLoggingSink dls; DummyBluetoothStateManager dbsm; - herald::Context ctx(dls,dbsm); // default context include - // using CT = typename herald::Context; + herald::DefaultPlatformType dpt; + herald::Context ctx(dpt,dls,dbsm); // default context include + // using CT = typename herald::Context; herald::data::SensorLogger logger(ctx.getLoggingSink(),"testout","mytest"); HTERR("Simple string"); @@ -122,8 +125,9 @@ TEST_CASE("sensorlogger-output-intrinsic", "[sensorlogger][output]") { SECTION("sensorlogger-output-intrinsic") { DummyLoggingSink dls; DummyBluetoothStateManager dbsm; - herald::Context ctx(dls,dbsm); // default context include - // using CT = typename herald::Context; + herald::DefaultPlatformType dpt; + herald::Context ctx(dpt,dls,dbsm); // default context include + // using CT = typename herald::Context; herald::data::SensorLogger logger(ctx.getLoggingSink(),"testout","mytest"); int i = 37; @@ -183,8 +187,9 @@ TEST_CASE("sensorlogger-bug-negativesuccess", "[sensorlogger][output][bug]") { SECTION("sensorlogger-bug-negativesuccess") { DummyLoggingSink dls; DummyBluetoothStateManager dbsm; - herald::Context ctx(dls,dbsm); // default context include - // using CT = typename herald::Context; + herald::DefaultPlatformType dpt; + herald::Context ctx(dpt,dls,dbsm); // default context include + // using CT = typename herald::Context; herald::data::SensorLogger logger(ctx.getLoggingSink(),"testout","mytest"); int success = -22; @@ -199,8 +204,9 @@ TEST_CASE("sensorlogger-bug-targetidatend", "[sensorlogger][output][bug]") { SECTION("sensorlogger-bug-targetidatend") { DummyLoggingSink dls; DummyBluetoothStateManager dbsm; - herald::Context ctx(dls,dbsm); // default context include - // using CT = typename herald::Context; + herald::DefaultPlatformType dpt; + herald::Context ctx(dpt,dls,dbsm); // default context include + // using CT = typename herald::Context; herald::data::SensorLogger logger(ctx.getLoggingSink(),"testout","mytest"); herald::datatype::TargetIdentifier t(herald::datatype::Data(std::byte(0x09),3)); @@ -216,8 +222,9 @@ TEST_CASE("sensorlogger-output-data", "[sensorlogger][output]") { SECTION("sensorlogger-output-data") { DummyLoggingSink dls; DummyBluetoothStateManager dbsm; - herald::Context ctx(dls,dbsm); // default context include - // using CT = typename herald::Context; + herald::DefaultPlatformType dpt; + herald::Context ctx(dpt,dls,dbsm); // default context include + // using CT = typename herald::Context; herald::data::SensorLogger logger(ctx.getLoggingSink(),"testout","mytest"); herald::datatype::Data t(std::byte(0x09),3); @@ -233,8 +240,9 @@ TEST_CASE("sensorlogger-output-targetidentifier", "[sensorlogger][output]") { SECTION("sensorlogger-output-targetidentifier") { DummyLoggingSink dls; DummyBluetoothStateManager dbsm; - herald::Context ctx(dls,dbsm); // default context include - // using CT = typename herald::Context; + herald::DefaultPlatformType dpt; + herald::Context ctx(dpt,dls,dbsm); // default context include + // using CT = typename herald::Context; herald::data::SensorLogger logger(ctx.getLoggingSink(),"testout","mytest"); herald::datatype::TargetIdentifier t(herald::datatype::Data(std::byte(0x09),3)); diff --git a/herald-tests/simplepayload-tests.cpp b/herald-tests/simplepayload-tests.cpp index 6e065c0..3d6eeb1 100644 --- a/herald-tests/simplepayload-tests.cpp +++ b/herald-tests/simplepayload-tests.cpp @@ -239,8 +239,9 @@ TEST_CASE("payload-simple-basic", "[payload][simple][basic]") { SECTION("payload-simple-basic") { DummyLoggingSink dls; DummyBluetoothStateManager dbsm; - herald::Context ctx(dls,dbsm); // default context include - // using CT = typename herald::Context; + herald::DefaultPlatformType dpt; + herald::Context ctx(dpt,dls,dbsm); // default context include + // using CT = typename herald::Context; std::uint16_t country = 826; std::uint16_t state = 4; herald::payload::simple::K k; @@ -278,8 +279,9 @@ TEST_CASE("payload-simple-payloadbounds", "[payload][simple][payloadbounds]") { SECTION("payload-simple-payloadbounds") { DummyLoggingSink dls; DummyBluetoothStateManager dbsm; - herald::Context ctx(dls,dbsm); // default context include - // using CT = typename herald::Context; + herald::DefaultPlatformType dpt; + herald::Context ctx(dpt,dls,dbsm); // default context include + // using CT = typename herald::Context; std::uint16_t country = 826; std::uint16_t state = 4; herald::payload::simple::K k; diff --git a/herald-tests/test-templates.h b/herald-tests/test-templates.h index d733f55..ac812d3 100644 --- a/herald-tests/test-templates.h +++ b/herald-tests/test-templates.h @@ -30,7 +30,7 @@ class DummyBluetoothStateManager : public herald::ble::BluetoothStateManager { DummyBluetoothStateManager() = default; ~DummyBluetoothStateManager() = default; - void add(std::shared_ptr delegate) override { + void add(herald::ble::BluetoothStateManagerDelegate& delegate) override { ; // no op } diff --git a/herald-wearable/.vscode/launch.json b/herald-wearable/.vscode/launch.json index 4637827..00a9d8a 100644 --- a/herald-wearable/.vscode/launch.json +++ b/herald-wearable/.vscode/launch.json @@ -16,7 +16,8 @@ "server": "C:/Program Files (x86)/SEGGER/JLink/JLinkGDBServerCL.exe", }, "serverArgs": [ - "-device", "NRF5340_XXAA_APP", + //"-device", "NRF5340_XXAA_APP", + "-device", "NRF52833_XXAA", "-if", "SWD", "-speed", "4000" ], diff --git a/herald-wearable/.vscode/settings.json b/herald-wearable/.vscode/settings.json index 55c9847..37cf401 100644 --- a/herald-wearable/.vscode/settings.json +++ b/herald-wearable/.vscode/settings.json @@ -61,5 +61,8 @@ "streambuf": "cpp", "thread": "cpp", "cinttypes": "cpp" + }, + "cmake.configureEnvironment": { + "BOARD": "nrf52833dk_nrf52833" } } \ No newline at end of file diff --git a/herald-wearable/src/main.cpp b/herald-wearable/src/main.cpp index 36fbb20..649c436 100644 --- a/herald-wearable/src/main.cpp +++ b/herald-wearable/src/main.cpp @@ -39,6 +39,8 @@ #include #include +#include + #include namespace applogging { LOG_MODULE_REGISTER(app, CONFIG_APP_LOG_LEVEL); @@ -183,9 +185,9 @@ void herald_entry() { // IMPLEMENTORS GUIDANCE - USING HERALD // First initialise the Zephyr Context - this links Herald to any Zephyr OS specific constructs or callbacks - herald::ZephyrContextProvider zcp; - herald::Context ctx(zcp.getLoggingSink(),zcp.getBluetoothStateManager()); - using CT = Context; + ZephyrContextProvider zcp; + Context ctx(zcp,zcp.getLoggingSink(),zcp.getBluetoothStateManager()); + using CT = Context; // Now prepare your device's Herald identity payload - this is what gets sent to other devices when they request it // SECURITY: Depending on the payload provider, this could be static and in the clear or varying over time. @@ -206,7 +208,7 @@ void herald_entry() { } else { APP_DBG("Couldn't read hardware info for zephyr device. Error code: %d", hwInfoAvailable); } - APP_DBG("Final clientID: %d", clientId); + APP_DBG("Final clientID: %" PRIu64 "", clientId); std::shared_ptr pds = std::make_shared( countryCode, @@ -263,13 +265,13 @@ void herald_entry() { k_sleep(K_SECONDS(2)); - auto sink = ctx.getLoggingSink(); + auto& sink = ctx.getLoggingSink(); sink.log("subsys1","cat1",SensorLoggerLevel::debug,"Here's some info for you"); auto payload = pds->payload(PayloadTimestamp(),nullptr); sink.log("subsys1","cat1",SensorLoggerLevel::debug,"I've got some payload data"); sink.log("subsys1","cat1",SensorLoggerLevel::debug,payload->hexEncodedString()); - auto sink2 = ctx.getLoggingSink(); + auto& sink2 = ctx.getLoggingSink(); sink2.log("subsys2","cat2",SensorLoggerLevel::debug,"Here's some more info for you"); // LOGGING LEVEL TESTING @@ -289,7 +291,7 @@ void herald_entry() { // Create Herald sensor array - this handles both advertising (Transmitter) and scanning/connecting (Receiver) SensorArray sa(ctx,pds); - ConcreteBLESensor,ConcreteBLEReceiver> ble(ctx,ctx.getBluetoothStateManager(),pds); + ConcreteBLESensor ble(ctx,ctx.getBluetoothStateManager(),pds); // Add contacts.log delegate // CURRENTLY BREAKS ZEPHYR - DON'T KNOW WHY YET - LOGGING SUBSYSTEM ISSUE @@ -305,20 +307,22 @@ void herald_entry() { // 4. Now create a live analysis pipeline and enable RSSI to be sent to it for distance estimation - // herald::analysis::algorithms::distance::FowlerBasicAnalyser distanceAnalyser(0, -50, -24); // 0 = run every time run() is called +#ifdef HERALD_ANALYSIS_ENABLED + herald::analysis::algorithms::distance::FowlerBasicAnalyser distanceAnalyser(0, -50, -24); // 0 = run every time run() is called - // herald::analysis::LoggingAnalysisDelegate myDelegate(ctx); - // herald::analysis::AnalysisDelegateManager adm(std::move(myDelegate)); // NOTE: myDelegate MOVED FROM and no longer accessible - // herald::analysis::AnalysisProviderManager apm(std::move(distanceAnalyser)); // NOTE: distanceAnalyser MOVED FROM and no longer accessible + herald::analysis::LoggingAnalysisDelegate myDelegate(ctx); + herald::analysis::AnalysisDelegateManager adm(std::move(myDelegate)); // NOTE: myDelegate MOVED FROM and no longer accessible + herald::analysis::AnalysisProviderManager apm(std::move(distanceAnalyser)); // NOTE: distanceAnalyser MOVED FROM and no longer accessible - // herald::analysis::AnalysisRunner< - // herald::analysis::AnalysisDelegateManager>, - // herald::analysis::AnalysisProviderManager, - // RSSI,Distance - // > runner(adm, apm); // just for Sample types, and their produced output (Sample) + herald::analysis::AnalysisRunner< + herald::analysis::AnalysisDelegateManager>, + herald::analysis::AnalysisProviderManager, + RSSI,Distance + > runner(adm, apm); // just for Sample types, and their produced output (Sample) - // std::shared_ptr> src = std::make_shared>(runner); - // sa->add(src); + std::shared_ptr> src = std::make_shared>(runner); + sa->add(src); +#endif APP_DBG("Starting sensor array"); @@ -373,6 +377,7 @@ void main(void) #endif // Start herald entry on a new thread in case of errors, or needing to do something on the main thread + [[maybe_unused]] k_tid_t herald_pid = k_thread_create(&herald_thread, herald_stack, 4096, (k_thread_entry_t)herald_entry, NULL, NULL, NULL, -1, K_USER, diff --git a/herald/include/herald/analysis/logging_analysis_delegate.h b/herald/include/herald/analysis/logging_analysis_delegate.h index 987abcb..c03913e 100644 --- a/herald/include/herald/analysis/logging_analysis_delegate.h +++ b/herald/include/herald/analysis/logging_analysis_delegate.h @@ -22,11 +22,11 @@ struct LoggingAnalysisDelegate { using value_type = ValT; - LoggingAnalysisDelegate() - : ctx(nullptr) - HLOGGERINIT(nullptr,"herald","LoggingAnalysisDelegate") - { - } + // LoggingAnalysisDelegate() + // : ctx(nullptr) + // HLOGGERINIT(nullptr,"herald","LoggingAnalysisDelegate") + // { + // } LoggingAnalysisDelegate(ContextT& ctx) : ctx(ctx) diff --git a/herald/include/herald/ble/ble_concrete.h b/herald/include/herald/ble/ble_concrete.h index cbde122..e3bd130 100644 --- a/herald/include/herald/ble/ble_concrete.h +++ b/herald/include/herald/ble/ble_concrete.h @@ -22,6 +22,16 @@ #include "ble_coordinator.h" #include "../datatype/bluetooth_state.h" +// Include the relevant concrete BLE Receiver here +#ifdef __ZEPHYR__ +#include "zephyr/concrete_ble_receiver.h" +#include "zephyr/concrete_ble_transmitter.h" +// TODO other platforms here +#else +#include "default/concrete_ble_receiver.h" +#include "default/concrete_ble_transmitter.h" +#endif + #include #include #include @@ -55,9 +65,9 @@ using namespace herald::payload; /** * Acts as the main object to control the receiver, transmitter, and database instances */ -template +template class ConcreteBLESensor : public BLESensor, public BLEDatabaseDelegate, - public BluetoothStateManagerDelegate, public std::enable_shared_from_this> { + public BluetoothStateManagerDelegate /*, public std::enable_shared_from_this>*/ { public: ConcreteBLESensor(ContextT& ctx, BluetoothStateManager& bluetoothStateManager, std::shared_ptr payloadDataSupplier) @@ -107,8 +117,8 @@ class ConcreteBLESensor : public BLESensor, public BLEDatabaseDelegate, void start() override { if (!addedSelfAsDelegate) { - stateManager.add(this->shared_from_this()); // FAILS IF USED IN THE CTOR - DO NOT DO THIS FROM CTOR - database.add(this->shared_from_this()); + stateManager.add(*this); // FAILS IF USED IN THE CTOR - DO NOT DO THIS FROM CTOR + database.add(*this); addedSelfAsDelegate = true; } transmitter.start(); @@ -216,14 +226,18 @@ class ConcreteBLESensor : public BLESensor, public BLEDatabaseDelegate, // Data members hidden by PIMPL - ConcreteBLEDatabase& database; + ConcreteBLEDatabase database; BluetoothStateManager& stateManager; - TransmitterT& transmitter; - ReceiverT& receiver; + ConcreteBLETransmitter> transmitter; + ConcreteBLEReceiver> receiver; std::vector> delegates; - HeraldProtocolBLECoordinationProvider,ReceiverT> coordinator; + HeraldProtocolBLECoordinationProvider< + ContextT, + ConcreteBLEDatabase, + ConcreteBLEReceiver> + > coordinator; bool addedSelfAsDelegate; diff --git a/herald/include/herald/ble/ble_concrete_database.h b/herald/include/herald/ble/ble_concrete_database.h index 30b9fef..2861f6b 100644 --- a/herald/include/herald/ble/ble_concrete_database.h +++ b/herald/include/herald/ble/ble_concrete_database.h @@ -60,8 +60,8 @@ class ConcreteBLEDatabase : public BLEDatabase, public BLEDeviceDelegate /*, pub // BLE Database overrides - void add(const std::shared_ptr& delegate) override { - delegates.push_back(delegate); + void add(BLEDatabaseDelegate& delegate) override { + delegates.emplace_back(delegate); } // Creation overrides @@ -157,8 +157,8 @@ class ConcreteBLEDatabase : public BLEDatabase, public BLEDeviceDelegate /*, pub newDevice->registerDiscovery(Date()); devices.push_back(newDevice); - for (auto delegate : delegates) { - delegate->bleDatabaseDidCreate(newDevice); + for (auto& delegate : delegates) { + delegate.get().bleDatabaseDidCreate(newDevice); } return newDevice; } @@ -181,8 +181,8 @@ class ConcreteBLEDatabase : public BLEDatabase, public BLEDeviceDelegate /*, pub std::shared_ptr newDevice = std::make_shared( TargetIdentifier(payloadData), *this); //this->shared_from_this()); devices.push_back(newDevice); - for (auto delegate : delegates) { - delegate->bleDatabaseDidCreate(newDevice); + for (auto& delegate : delegates) { + delegate.get().bleDatabaseDidCreate(newDevice); } return newDevice; } @@ -197,8 +197,8 @@ class ConcreteBLEDatabase : public BLEDatabase, public BLEDeviceDelegate /*, pub std::shared_ptr newDevice = std::make_shared( targetIdentifier, *this);// this->shared_from_this()); devices.push_back(newDevice); - for (auto delegate : delegates) { - delegate->bleDatabaseDidCreate(newDevice); + for (auto& delegate : delegates) { + delegate.get().bleDatabaseDidCreate(newDevice); } return newDevice; } @@ -233,7 +233,7 @@ class ConcreteBLEDatabase : public BLEDatabase, public BLEDeviceDelegate /*, pub std::shared_ptr toRemove = *found; devices.erase(found); for (auto& delegate : delegates) { - delegate->bleDatabaseDidDelete(toRemove); + delegate.get().bleDatabaseDidDelete(toRemove); } } } @@ -244,7 +244,7 @@ class ConcreteBLEDatabase : public BLEDatabase, public BLEDeviceDelegate /*, pub void device(const std::shared_ptr& device, BLEDeviceAttribute didUpdate) override { // TODO update any internal DB state as necessary (E.g. deletion) for (auto& delegate : delegates) { - delegate->bleDatabaseDidUpdate(device, didUpdate); // TODO verify this is the right onward call + delegate.get().bleDatabaseDidUpdate(device, didUpdate); // TODO verify this is the right onward call } } @@ -318,7 +318,7 @@ class ConcreteBLEDatabase : public BLEDatabase, public BLEDeviceDelegate /*, pub } ContextT& ctx; - std::vector> delegates; + std::vector> delegates; std::vector> devices; HLOGGER(ContextT); diff --git a/herald/include/herald/ble/ble_database.h b/herald/include/herald/ble/ble_database.h index 98bb713..932ff08 100644 --- a/herald/include/herald/ble/ble_database.h +++ b/herald/include/herald/ble/ble_database.h @@ -27,7 +27,7 @@ class BLEDatabase { BLEDatabase() = default; virtual ~BLEDatabase() = default; - virtual void add(const std::shared_ptr& delegate) = 0; + virtual void add(BLEDatabaseDelegate& delegate) = 0; virtual std::shared_ptr device(const BLEMacAddress& mac, const Data& advert/*, const RSSI& rssi*/) = 0; diff --git a/herald/include/herald/ble/bluetooth_state_manager.h b/herald/include/herald/ble/bluetooth_state_manager.h index a855715..8ed2520 100644 --- a/herald/include/herald/ble/bluetooth_state_manager.h +++ b/herald/include/herald/ble/bluetooth_state_manager.h @@ -21,7 +21,7 @@ class BluetoothStateManager { BluetoothStateManager() = default; virtual ~BluetoothStateManager() = default; - virtual void add(std::shared_ptr delegate) = 0; + virtual void add(BluetoothStateManagerDelegate& delegate) = 0; virtual BluetoothState state() = 0; }; diff --git a/herald/include/herald/ble/default/concrete_ble_receiver.h b/herald/include/herald/ble/default/concrete_ble_receiver.h new file mode 100644 index 0000000..6f11011 --- /dev/null +++ b/herald/include/herald/ble/default/concrete_ble_receiver.h @@ -0,0 +1,54 @@ +// Copyright 2020-2021 Herald Project Contributors +// SPDX-License-Identifier: Apache-2.0 +// + +#ifndef HERALD_DEFAULT_BLE_CONCRETE_RECEIVER_H +#define HERALD_DEFAULT_BLE_CONCRETE_RECEIVER_H + +#include "../bluetooth_state_manager.h" +#include "../ble_receiver.h" +#include "../../payload/payload_data_supplier.h" +#include "../../datatype/data.h" +#include "../../datatype/target_identifier.h" + +#include + +namespace herald { +namespace ble { + +using namespace herald::datatype; +using namespace herald::payload; + +/// \brief Dummy implementation of a ConcreteBLEReceiver that does nothing (used for testing) +template +class ConcreteBLEReceiver : public BLEReceiver, public HeraldProtocolV1Provider { +public: + ConcreteBLEReceiver(ContextT& ctx, BluetoothStateManager& bluetoothStateManager, + std::shared_ptr payloadDataSupplier, BLEDatabaseT& bleDatabase) {} + ConcreteBLEReceiver(const ConcreteBLEReceiver& from) = delete; + ConcreteBLEReceiver(ConcreteBLEReceiver&& from) = delete; + ~ConcreteBLEReceiver() {} + + // Coordination overrides - Since v1.2-beta3 + std::optional> coordinationProvider() override { + return {}; + } + + bool immediateSend(Data data, const TargetIdentifier& targetIdentifier) override { + return false; + } + bool immediateSendAll(Data data) override { + return false; + } + + // Sensor overrides + void add(const std::shared_ptr& delegate) override {} + void start() override {} + void stop() override {} + +}; + +} +} + +#endif \ No newline at end of file diff --git a/herald/include/herald/ble/default/concrete_ble_transmitter.h b/herald/include/herald/ble/default/concrete_ble_transmitter.h new file mode 100644 index 0000000..8486591 --- /dev/null +++ b/herald/include/herald/ble/default/concrete_ble_transmitter.h @@ -0,0 +1,68 @@ +// Copyright 2020-2021 Herald Project Contributors +// SPDX-License-Identifier: Apache-2.0 +// + +#ifndef HERALD_DEFAULT_BLE_CONCRETE_TRANSMITTER_H +#define HERALD_DEFAULT_BLE_CONCRETE_TRANSMITTER_H + +#include "../ble_database.h" +#include "../ble_receiver.h" +#include "../ble_sensor.h" +#include "../ble_transmitter.h" +#include "../ble_concrete.h" +#include "../ble_protocols.h" +#include "../bluetooth_state_manager.h" +#include "../ble_device_delegate.h" +#include "../filter/ble_advert_parser.h" +#include "../../payload/payload_data_supplier.h" +#include "../../context.h" +#include "../../data/sensor_logger.h" +#include "../ble_sensor_configuration.h" +#include "../ble_coordinator.h" +#include "../../datatype/bluetooth_state.h" + +// C++17 includes +#include +#include +#include +#include +#include + +namespace herald { +namespace ble { + +using namespace herald::datatype; +using namespace herald::ble::filter; +using namespace herald::payload; + + +/// \brief Dummy implementation of a ConcreteBLETransmitter that does nothing (used for testing) +template +class ConcreteBLETransmitter : public BLETransmitter { +public: + ConcreteBLETransmitter(ContextT& ctx, BluetoothStateManager& bluetoothStateManager, + std::shared_ptr payloadDataSupplier, BLEDatabaseT& bleDatabase) {} + + ConcreteBLETransmitter(const ConcreteBLETransmitter& from) = delete; + ConcreteBLETransmitter(ConcreteBLETransmitter&& from) = delete; + + ~ConcreteBLETransmitter() {} + + // Coordination overrides - Since v1.2-beta3 + std::optional> coordinationProvider() override { + return {}; + } + + // Sensor overrides + void add(const std::shared_ptr& delegate) override {} + + void start() override {} + + void stop() override {} + +}; + +} +} + +#endif \ No newline at end of file diff --git a/herald/include/herald/ble/zephyr/concrete_ble_receiver.h b/herald/include/herald/ble/zephyr/concrete_ble_receiver.h index fc2425d..425b5f7 100644 --- a/herald/include/herald/ble/zephyr/concrete_ble_receiver.h +++ b/herald/include/herald/ble/zephyr/concrete_ble_receiver.h @@ -9,7 +9,6 @@ #include "../ble_receiver.h" #include "../ble_sensor.h" #include "../ble_transmitter.h" -#include "../ble_concrete.h" #include "../ble_protocols.h" #include "../bluetooth_state_manager.h" #include "../ble_device_delegate.h" @@ -20,11 +19,28 @@ #include "../ble_sensor_configuration.h" #include "../ble_coordinator.h" #include "../../datatype/bluetooth_state.h" +#include "../ble_mac_address.h" +#include "../../zephyr_context.h" +// nRF Connect SDK includes +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// C++17 includes #include #include #include #include +#include +#include namespace herald { namespace ble { @@ -33,25 +49,395 @@ using namespace herald::datatype; using namespace herald::ble::filter; using namespace herald::payload; -template -class ConcreteBLEReceiver : public BLEReceiver, public HeraldProtocolV1Provider, public std::enable_shared_from_this> { + +// ZEPHYR UTILITY FUNCTIONS +/** wait with timeout for Zephyr. Returns true if the function timed out rather than completed **/ +uint32_t waitWithTimeout(uint32_t timeoutMillis, k_timeout_t period, std::function keepWaiting) +{ + uint32_t start_time; + uint32_t stop_time; + uint32_t millis_spent; + bool notComplete = keepWaiting(); + if (!notComplete) { + return 0; + } + /* capture initial time stamp */ + start_time = k_uptime_get_32(); + + /* capture final time stamp */ + stop_time = k_uptime_get_32(); + /* compute how long the work took (assumes no counter rollover) */ + millis_spent = stop_time - start_time; + + while (millis_spent < timeoutMillis && notComplete) { + k_sleep(period); + notComplete = keepWaiting(); + + /* capture final time stamp */ + stop_time = k_uptime_get_32(); + /* compute how long the work took (assumes no counter rollover) */ + millis_spent = stop_time - start_time; + } + if (notComplete) { + return millis_spent; + } else { + return 0; + } +} + +struct ConnectedDeviceState { + ConnectedDeviceState(const TargetIdentifier& id) + : target(id), state(BLEDeviceState::disconnected), connection(NULL), address(), + readPayload(), immediateSend(), remoteInstigated(false) + {} + ConnectedDeviceState(const ConnectedDeviceState& from) = delete; + ConnectedDeviceState(ConnectedDeviceState&& from) = delete; + ~ConnectedDeviceState() = default; + + TargetIdentifier target; + BLEDeviceState state; + bt_conn* connection; + bt_addr_le_t address; + PayloadData readPayload; + ImmediateSendData immediateSend; + bool remoteInstigated; +}; + +namespace zephyrinternal { + + /* Herald Service Variables */ + static struct bt_uuid_128 herald_uuid = BT_UUID_INIT_128( + 0x9b, 0xfd, 0x5b, 0xd6, 0x72, 0x45, 0x1e, 0x80, 0xd3, 0x42, 0x46, 0x47, 0xaf, 0x32, 0x81, 0x42 + ); + static struct bt_uuid_128 herald_char_signal_android_uuid = BT_UUID_INIT_128( + 0x11, 0x1a, 0x82, 0x80, 0x9a, 0xe0, 0x24, 0x83, 0x7a, 0x43, 0x2e, 0x09, 0x13, 0xb8, 0x17, 0xf6 + ); + static struct bt_uuid_128 herald_char_signal_ios_uuid = BT_UUID_INIT_128( + 0x63, 0x43, 0x2d, 0xb0, 0xad, 0xa4, 0xf3, 0x8a, 0x9a, 0x4a, 0xe4, 0xea, 0xf2, 0xd5, 0xb0, 0x0e + ); + static struct bt_uuid_128 herald_char_payload_uuid = BT_UUID_INIT_128( + 0xe7, 0x33, 0x89, 0x8f, 0xe3, 0x43, 0x21, 0xa1, 0x29, 0x48, 0x05, 0x8f, 0xf8, 0xc0, 0x98, 0x3e + ); + + + bt_le_conn_param* BTLEConnParam = BT_LE_CONN_PARAM_DEFAULT; // BT_LE_CONN_PARAM(0x018,3200,0,400); // NOT BT_LE_CONN_PARAM_DEFAULT; + bt_conn_le_create_param* BTLECreateParam = BT_CONN_LE_CREATE_CONN; // BT_CONN_LE_CREATE_PARAM(BT_CONN_LE_OPT_NONE, 0x0010,0x0010);// NOT BT_CONN_LE_CREATE_CONN; + + static struct bt_conn_le_create_param defaultCreateParam = BT_CONN_LE_CREATE_PARAM_INIT( + BT_CONN_LE_OPT_NONE, BT_GAP_SCAN_FAST_INTERVAL, BT_GAP_SCAN_FAST_INTERVAL + ); + static struct bt_le_conn_param defaultConnParam = BT_LE_CONN_PARAM_INIT( + //BT_GAP_INIT_CONN_INT_MIN, BT_GAP_INIT_CONN_INT_MAX, 0, 400 + //12, 12 // aka 15ms, default from apple documentation + 0x50, 0x50, // aka 80ms, from nRF SDK LLPM sample + 0, 400 + ); + // Note for apple see: https://developer.apple.com/library/archive/qa/qa1931/_index.html + // And https://developer.apple.com/accessories/Accessory-Design-Guidelines.pdf (BLE section) + + [[maybe_unused]] + static struct bt_le_scan_param defaultScanParam = //BT_LE_SCAN_PASSIVE; + { + .type = BT_LE_SCAN_TYPE_PASSIVE, // passive scan + .options = BT_LE_SCAN_OPT_FILTER_DUPLICATE, // Scans for EVERYTHING + .interval = BT_GAP_SCAN_FAST_INTERVAL, // 0x0010, // V.FAST, NOT BT_GAP_SCAN_FAST_INTERVAL - gap.h + .window = BT_GAP_SCAN_FAST_WINDOW // 0x0010, // V.FAST, NOT BT_GAP_SCAN_FAST_INTERVAL - gap.h + }; + + /** + * Why is this necessary? Traditional pointer-to-function cannot easily + * and reliably be wrapped with std::function/bind/mem_fn. We also need + * the Herald API to use subclasses for each platform, necessitating + * some sort of static bridge. Not pretty, but works and allows us to + * prevent nullptr problems + */ + std::optional> + concreteReceiverInstance; + + + + // static struct bt_conn* conn = NULL; + + [[maybe_unused]] + // NOTE: The below is called multiple times for ONE char value. Keep appending to result until NULL==data. + static uint8_t gatt_read_cb(struct bt_conn *conn, uint8_t err, + struct bt_gatt_read_params *params, + const void *data, uint16_t length) + { + if (concreteReceiverInstance.has_value()) { + return concreteReceiverInstance.value().get().gatt_read_cb(conn,err,params,data,length); + } + return length; // say we've consumed the data anyway + } + + [[maybe_unused]] + static struct bt_gatt_read_params read_params = { + .func = gatt_read_cb, + .handle_count = 1, + .single = { + .handle = 0x0000, + .offset = 0x0000 + } + }; + + + // void scan_init(void) + // { + // // int err; + + // // struct bt_scan_init_param scan_init = { + // // .connect_if_match = 0, // no auto connect (handled by herald protocol coordinator) + // // .scan_param = NULL, + // // .conn_param = BT_LE_CONN_PARAM_DEFAULT + // // }; + + // // bt_scan_init(&scan_init); + // // bt_scan_cb_register(&scan_cb); + + // /* + // err = bt_scan_filter_add(BT_SCAN_FILTER_TYPE_UUID, herald_uuid); + // if (err) { + // printk("Scanning filters cannot be set (err %d)\n", err); + + // return; + // } + + // err = bt_scan_filter_enable(BT_SCAN_UUID_FILTER, false); + // if (err) { + // printk("Filters cannot be turned on (err %d)\n", err); + // } + // */ + // } + + + [[maybe_unused]] + static void connected(struct bt_conn *conn, uint8_t err) + { + if (concreteReceiverInstance.has_value()) { + concreteReceiverInstance.value().get().connected(conn,err); + } + } + + [[maybe_unused]] + static void disconnected(struct bt_conn *conn, uint8_t reason) + { + if (concreteReceiverInstance.has_value()) { + concreteReceiverInstance.value().get().disconnected(conn,reason); + } + } + + [[maybe_unused]] + static void le_param_updated(struct bt_conn *conn, uint16_t interval, + uint16_t latency, uint16_t timeout) + { + if (concreteReceiverInstance.has_value()) { + concreteReceiverInstance.value().get().le_param_updated(conn,interval,latency,timeout); + } + } + + [[maybe_unused]] + static struct bt_conn_cb conn_callbacks = { + .connected = connected, + .disconnected = disconnected, + .le_param_updated = le_param_updated, + }; + + // static bt_addr_le_t *last_addr = BT_ADDR_LE_NONE; + + [[maybe_unused]] + void scan_cb(const bt_addr_le_t *addr, int8_t rssi, uint8_t adv_type, + struct net_buf_simple *buf) { + if (concreteReceiverInstance.has_value()) { + concreteReceiverInstance.value().get().scan_cb(addr,rssi,adv_type,buf); + } + } + + // BT_SCAN_CB_INIT(scan_cbs, scan_filter_match, ); + + [[maybe_unused]] + static struct bt_scan_init_param scan_init = { + .scan_param = &defaultScanParam, + .connect_if_match = false, + .conn_param = &defaultConnParam + }; + + // void scan_filter_match(struct bt_scan_device_info *device_info, + // struct bt_scan_filter_match *filter_match, + // bool connectable) + // { + // char addr[BT_ADDR_LE_STR_LEN]; + + // bt_addr_le_to_str(device_info->recv_info->addr, addr, sizeof(addr)); + + // printk("Filters matched. Address: %s connectable: %s\n", + // addr, connectable ? "yes" : "no"); + // } + + // void scan_connecting_error(struct bt_scan_device_info *device_info) + // { + // printk("Connecting failed\n"); + // } + + // void scan_connecting(struct bt_scan_device_info *device_info, + // struct bt_conn *conn) + // { + // //default_conn = bt_conn_ref(conn); + // } + + // void scan_filter_no_match(struct bt_scan_device_info *device_info, + // bool connectable) + // { + // int err; + // struct bt_conn *conn; + // char addr[BT_ADDR_LE_STR_LEN]; + + // if (device_info->recv_info->adv_type == BT_GAP_ADV_TYPE_ADV_DIRECT_IND) { + // bt_addr_le_to_str(device_info->recv_info->addr, addr, + // sizeof(addr)); + // printk("Direct advertising received from %s\n", addr); + // bt_scan_stop(); + + // err = bt_conn_le_create(device_info->recv_info->addr, + // BT_CONN_LE_CREATE_CONN, + // device_info->conn_param, &conn); + + // if (!err) { + // default_conn = bt_conn_ref(conn); + // bt_conn_unref(conn); + // } + // } + // } + + // BT_SCAN_CB_INIT(scan_cb, scan_filter_match, scan_filter_no_match, + // scan_connecting_error, scan_connecting); + + + + // GATT DISCOVERY INTERNAL METHODS + + static void discovery_completed_cb(struct bt_gatt_dm *dm, + void *context) + { + if (concreteReceiverInstance.has_value()) { + concreteReceiverInstance.value().get().discovery_completed_cb(dm,context); + } + } + + static void discovery_service_not_found_cb(struct bt_conn *conn, + void *context) + { + if (concreteReceiverInstance.has_value()) { + concreteReceiverInstance.value().get().discovery_service_not_found_cb(conn,context); + } + } + + static void discovery_error_found_cb(struct bt_conn *conn, + int err, + void *context) + { + if (concreteReceiverInstance.has_value()) { + concreteReceiverInstance.value().get().discovery_error_found_cb(conn,err,context); + } + } + + static const struct bt_gatt_dm_cb discovery_cb = { + .completed = discovery_completed_cb, + .service_not_found = discovery_service_not_found_cb, + .error_found = discovery_error_found_cb, + }; + +} + +template +class ConcreteBLEReceiver : public BLEReceiver, public HeraldProtocolV1Provider, public herald::zephyrinternal::Callbacks /*, public std::enable_shared_from_this>*/ { public: ConcreteBLEReceiver(ContextT& ctx, BluetoothStateManager& bluetoothStateManager, - std::shared_ptr payloadDataSupplier, std::shared_ptr bleDatabase); + std::shared_ptr payloadDataSupplier, BLEDatabaseT& bleDatabase) + : m_context(ctx), // Herald API guarantees this to be safe + m_stateManager(bluetoothStateManager), + m_pds(payloadDataSupplier), + db(bleDatabase), + delegates(), + connectionStates(), + isScanning(false) + HLOGGERINIT(ctx,"Sensor","BLE.ConcreteBLEReceiver") + { + ; + } + ConcreteBLEReceiver(const ConcreteBLEReceiver& from) = delete; ConcreteBLEReceiver(ConcreteBLEReceiver&& from) = delete; - ~ConcreteBLEReceiver(); + + ~ConcreteBLEReceiver() { + ; + } // Coordination overrides - Since v1.2-beta3 - std::optional> coordinationProvider() override; + std::optional> coordinationProvider() override + { + return {}; // we don't provide this, ConcreteBLESensor provides this. We provide HeraldV1ProtocolProvider + } - bool immediateSend(Data data, const TargetIdentifier& targetIdentifier) override; - bool immediateSendAll(Data data) override; + bool immediateSend(Data data, const TargetIdentifier& targetIdentifier) override + { + return false; + } + + bool immediateSendAll(Data data) override { + return false; + } // Sensor overrides - void add(const std::shared_ptr& delegate) override; - void start() override; - void stop() override; + void add(const std::shared_ptr& delegate) override + { + delegates.push_back(delegate); + } + + void start() override + { + HDBG("ConcreteBLEReceiver::start"); + if (!BLESensorConfiguration::scanningEnabled) { + HDBG("Sensor Configuration has scanning disabled. Returning."); + return; + } + herald::ble::zephyrinternal::concreteReceiverInstance.emplace(*this); + + + // Ensure our zephyr context has bluetooth ready + HDBG("calling start bluetooth"); + int startOk = m_context.getPlatform().startBluetooth(); + HDBG("start bluetooth done"); + if (0 != startOk) { + HDBG("ERROR starting context bluetooth:-"); + HDBG(std::to_string(startOk)); + } + + HDBG("Calling conn cb register"); + bt_conn_cb_register(&zephyrinternal::conn_callbacks); + HDBG("conn cb register done"); + + HDBG("calling bt scan start"); + startScanning(); + + HDBG("ConcreteBLEReceiver::start completed successfully"); + } + + void stop() override + { + HDBG("ConcreteBLEReceiver::stop"); + if (!BLESensorConfiguration::scanningEnabled) { + HDBG("Sensor Configuration has scanning disabled. Returning."); + return; + } + + herald::ble::zephyrinternal::concreteReceiverInstance.reset(); // destroys the shared_ptr not necessarily the underlying value + + stopScanning(); + + // Don't stop Bluetooth altogether - this is done by the ZephyrContext->stopBluetooth() function only + + HDBG("ConcreteBLEReceiver::stop completed successfully"); + } // Herald V1 protocol provider overrides // C++17 CALLBACK VERSION:- @@ -63,17 +449,645 @@ class ConcreteBLEReceiver : public BLEReceiver, public HeraldProtocolV1Provider, // void immediateSendAll(Activity, CompletionCallback) override; // NON C++17 VERSION:- - bool openConnection(const TargetIdentifier& toTarget) override; - bool closeConnection(const TargetIdentifier& toTarget) override; - void restartScanningAndAdvertising() override; - std::optional serviceDiscovery(Activity) override; - std::optional readPayload(Activity) override; - std::optional immediateSend(Activity) override; - std::optional immediateSendAll(Activity) override; - -private: - class Impl; - std::shared_ptr mImpl; // shared to allow static callbacks to be bound + bool openConnection(const TargetIdentifier& toTarget) override + { + HDBG("openConnection"); + + // Create addr from TargetIdentifier data + ConnectedDeviceState& state = findOrCreateState(toTarget); + uint8_t val[6] = {0,0,0,0,0,0}; + Data addrData = (Data)toTarget; // TODO change this to mac for target ID + uint8_t t; + bool cok = addrData.uint8(0,t); + if (cok) + val[0] = t; + cok = addrData.uint8(1,t); + if (cok) + val[1] = t; + cok = addrData.uint8(2,t); + if (cok) + val[2] = t; + cok = addrData.uint8(3,t); + if (cok) + val[3] = t; + cok = addrData.uint8(4,t); + if (cok) + val[4] = t; + cok = addrData.uint8(5,t); + if (cok) + val[5] = t; + // TODO create a convenience function in Data for the above + + // TODO don't assume RANDOM (1) in the below + bt_addr_le_t tempAddress{1, {{val[0],val[1],val[2],val[3],val[4],val[5]}}}; + // state.address = BT_ADDR_LE_NONE; + bt_addr_le_copy(&state.address, &tempAddress); + HDBG("Address copied. Constituted as:-"); + // idiot check of copied data + Data newAddr(state.address.a.val,6); + BLEMacAddress newMac(newAddr); + HDBG((std::string)newMac); + + + + + // // print out device info + // BLEMacAddress mac(addrData); + // std::string di("Opening Connection :: Device info: mac="); + // di += (std::string)mac; + // di += ", os="; + // auto devPtr = db.device(toTarget); + // auto os = devPtr->operatingSystem(); + // if (os.has_value()) { + // if (herald::ble::BLEDeviceOperatingSystem::ios == os) { + // di += "ios"; + // } else if (herald::ble::BLEDeviceOperatingSystem::android == os) { + // di += "android"; + // } + // } else { + // di += "unknown"; + // } + // di += ", ignore="; + // auto ignore = devPtr->ignore(); + // if (ignore) { + // di += "true"; + // } else { + // di += "false"; + // } + + // HDBG(di); + + + // temporarily stop scan - WORKAROUND for https://github.com/zephyrproject-rtos/zephyr/issues/20660 + // HDBG("pausing scanning"); + stopScanning(); + m_context.getPlatform().getAdvertiser().stopAdvertising(); + // HDBG("Scanning paused"); + + + // attempt connection, if required + bool ok = true; + if (NULL == state.connection) { + HDBG(" - No existing connection. Attempting to connect"); + // std::stringstream os; + // os << " - Create Param: Interval: " << zephyrinternal::defaultCreateParam.interval + // << ", Window: " << zephyrinternal::defaultCreateParam.window + // << ", Timeout: " << zephyrinternal::defaultCreateParam.timeout + // << " | Conn Param: Interval Min: " << zephyrinternal::BTLEConnParam->interval_min + // << ", Interval Max: " << zephyrinternal::defaultConnParam.interval_max + // << ", latency: " << zephyrinternal::defaultConnParam.latency + // << ", timeout: " << zephyrinternal::defaultConnParam.timeout + // << std::ends + // ; + // HDBG(os.str()); + // HDBG(std::to_string(zephyrinternal::defaultCreateParam.interval)); + // HDBG(std::to_string(zephyrinternal::defaultCreateParam.window)); + // HDBG(std::to_string(zephyrinternal::defaultCreateParam.timeout)); + // HDBG(std::to_string(zephyrinternal::defaultConnParam.interval_min)); + // HDBG(std::to_string(zephyrinternal::defaultConnParam.interval_max)); + // HDBG(std::to_string(zephyrinternal::defaultConnParam.latency)); + // HDBG(std::to_string(zephyrinternal::defaultConnParam.timeout)); + // HDBG("Random address check ok?"); + // HDBG(bt_le_scan_random_addr_check() ? "yes" : "no"); + + char addr_str[BT_ADDR_LE_STR_LEN]; + bt_addr_le_to_str(&state.address, addr_str, sizeof(addr_str)); + HDBG("ADDR AS STRING in openConnection:-"); + HDBG(addr_str); + + state.state = BLEDeviceState::connecting; // this is used by the condition variable + state.remoteInstigated = false; // as we're now definitely the instigators + int success = bt_conn_le_create( + &state.address, + &zephyrinternal::defaultCreateParam, + &zephyrinternal::defaultConnParam, + &state.connection + ); + HDBG(" - post connection attempt"); + if (0 != success) { + ok = false; + if (-EINVAL == success) { + HDBG(" - ERROR in passed in parameters"); + } else if (-EAGAIN == success) { + HDBG(" - bt device not ready"); + } else if (-EALREADY == success) { + HDBG(" - bt device initiating") + } else if (-ENOMEM == success) { + HDBG(" - bt connect attempt failed with default BT ID. Trying again later."); + // auto device = db.device(toTarget); + // device->ignore(true); + } else if (-ENOBUFS == success) { + HDBG(" - bt_hci_cmd_create has no buffers free"); + } else if (-ECONNREFUSED == success) { + HDBG(" - Connection refused"); + } else if (-EIO == success) { + HDBG(" - Low level BT HCI opcode IO failure"); + } else { + HDBG(" - Unknown error code..."); + HDBG(std::to_string(success)); + } + + // Add to ignore list for now + // DONT DO THIS HERE - MANY REASONS IT CAN FAIL auto device = db.device(toTarget); + // HDBG(" - Ignoring following target: {}", toTarget); + // device->ignore(true); + + // Log last disconnected time in BLE database (records failure, allows progressive backoff) + auto device = db.device(newMac); // Find by actual current physical address + device->state(BLEDeviceState::disconnected); + + // Immediately restart advertising on failure, but not scanning + m_context.getPlatform().getAdvertiser().startAdvertising(); + + return false; + } else { + HDBG("Zephyr waitWithTimeout for new connection"); + // lock and wait for connection to be created + + // STD::ASYNC/MUTEX variant:- + // std::unique_lock lk(bleInUse); + // connectionAvailable.wait(lk, [this] { + // return connectionState == BLEDeviceState::connecting; + // }); // BLOCKS + // verify connection successful + // connCallback(toTarget,connectionState == BLEDeviceState::connected); + + // ZEPHYR SPECIFIC VARIANT + uint32_t timedOut = waitWithTimeout(5'000, K_MSEC(25), [&state] { + return state.state == BLEDeviceState::connecting; + }); + if (timedOut != 0) { + HDBG("ZEPHYR WAIT TIMED OUT. Is connected?"); + HDBG((state.state == BLEDeviceState::connected) ? "true" : "false"); + HDBG(std::to_string(timedOut)); + return false; + } + // return connectionState == BLEDeviceState::connected; + return state.state == BLEDeviceState::connected; + } + } else { + HDBG(" - Existing connection exists! Reusing."); + return true; + } + } + + + + bool closeConnection(const TargetIdentifier& toTarget) override + { + HDBG("closeConnection call for ADDR:-"); + ConnectedDeviceState& state = findOrCreateState(toTarget); + char addr_str[BT_ADDR_LE_STR_LEN]; + bt_addr_le_to_str(&state.address, addr_str, sizeof(addr_str)); + HDBG(addr_str); + if (NULL != state.connection) { + if (state.remoteInstigated) { + HDBG("Connection remote instigated - not forcing close"); + } else { + bt_conn_disconnect(state.connection, BT_HCI_ERR_REMOTE_USER_TERM_CONN); + // auto device = db.device(toTarget); + // device->registerDisconnect(Date()); + } + } else { + // Can clear the remote instigated flag as they've closed the connection + state.remoteInstigated = false; + } + if (!state.remoteInstigated) { + removeState(toTarget); + return false; // assumes we've closed it // TODO proper multi-connection state tracking + } + return true; // remote instigated the connection - keep it open and inform caller + } + + + + void restartScanningAndAdvertising() override + { + // Print out current list of devices and their info + if (!connectionStates.empty()) { + HDBG("Current connection states cached:-"); + for (auto& [key,value] : connectionStates) { + std::string ci = " - "; + ci += ((Data)value.target).hexEncodedString(); + ci += " state: "; + switch (value.state) { + case BLEDeviceState::connected: + ci += "connected"; + break; + case BLEDeviceState::disconnected: + ci += "disconnected"; + break; + default: + ci += "connecting"; + } + ci += " connection is null: "; + ci += (NULL == value.connection ? "true" : "false"); + HDBG(ci); + + // Check connection reference is valid by address - has happened with non connectable devices (VR headset bluetooth stations) + value.connection = bt_conn_lookup_addr_le(BT_ID_DEFAULT, &value.address); + // If the above returns null, the next iterator will remove our state + + // Check for non null connection but disconnected state + + if (BLEDeviceState::disconnected == value.state) { + value.connection = NULL; + } + // Now check for timeout - nRF Connect doesn't cause a disconnect callback + if (NULL != value.connection && value.remoteInstigated) { + HDBG("REMOTELY INSTIGATED OR CONNECTED DEVICE TIMED OUT"); + auto device = db.device(value.target); + if (device->timeIntervalSinceConnected() < TimeInterval::never() && + device->timeIntervalSinceConnected() > TimeInterval::seconds(30)) { + // disconnect + bt_conn_disconnect(value.connection, BT_HCI_ERR_REMOTE_USER_TERM_CONN); + value.connection = NULL; + } + } + } + + // Do internal clean up too - remove states no longer required + for (auto iter = connectionStates.begin();connectionStates.end() != iter; ++iter) { + if (NULL == iter->second.connection) { // means Zephyr callbacks are finished with the connection object (i.e. disconnect was called) + connectionStates.erase(iter); + } + } + } + + // Restart scanning + // HDBG("restartScanningAndAdvertising - requesting scanning and advertising restarts"); + startScanning(); + m_context.getPlatform().getAdvertiser().startAdvertising(); + } + + std::optional serviceDiscovery(Activity activity) override + { + auto currentTargetOpt = std::get<1>(activity.prerequisites.front()); + if (!currentTargetOpt.has_value()) { + HDBG("No target specified for serviceDiscovery activity. Returning."); + return {}; // We've been asked to connect to no specific target - not valid for Bluetooth + } + // Ensure we have a cached state (i.e. we are connected) + auto& state = findOrCreateState(currentTargetOpt.value()); + if (state.state != BLEDeviceState::connected) { + HDBG("Not connected to target of activity. Returning."); + return {}; + } + if (NULL == state.connection) { + HDBG("State for activity does not have a connection. Returning."); + return {}; + } + auto device = db.device(currentTargetOpt.value()); + + gatt_discover(state.connection); + + uint32_t timedOut = waitWithTimeout(5'000, K_MSEC(25), [&device] () -> bool { + return !device->hasServicesSet(); // service discovery not completed yet + }); + + if (0 != timedOut) { + HDBG("service discovery timed out for device"); + HDBG(std::to_string(timedOut)); + return {}; + } + return {}; + } + + std::optional readPayload(Activity activity) override + { + return {}; + } + + std::optional immediateSend(Activity activity) override + { + return {}; + } + std::optional immediateSendAll(Activity activity) override + { + return {}; + } + +private: + // Zephyr OS callbacks + void scan_cb(const bt_addr_le_t *addr, int8_t rssi, uint8_t adv_type, + struct net_buf_simple *buf) override + { + // identify device by both MAC and potential pseudoDeviceAddress + BLEMacAddress bleMacAddress(addr->a.val); + Data advert(buf->data,buf->len); + auto device = db.device(bleMacAddress,advert); + + // auto device = db.device(target); + if (device->ignore()) { + // device->rssi(RSSI(rssi)); // TODO should we do this so our update date works and shows this as a 'live' device? + return; + } + + // // Now pass to relevant BLEDatabase API call + if (!device->rssi().has_value()) { + char addr_str[BT_ADDR_LE_STR_LEN]; + bt_addr_le_to_str(addr, addr_str, sizeof(addr_str)); + std::string addrStr(addr_str); + HTDBG("New address FROM SCAN:-"); + HTDBG(addr_str); + } + + // Add this RSSI reading - called at the end to ensure all other data variables set + device->rssi(RSSI(rssi)); + } + + void le_param_updated(struct bt_conn *conn, uint16_t interval, + uint16_t latency, uint16_t timeout) override + { + HTDBG("le param updated called"); + } + + void connected(struct bt_conn *conn, uint8_t err) override + { + HTDBG("**************** Zephyr connection callback. Mac of connected:"); + + auto addr = bt_conn_get_dst(conn); + char addr_str[BT_ADDR_LE_STR_LEN]; + bt_addr_le_to_str(addr, addr_str, sizeof(addr_str)); + std::string addrStr(addr_str); + BLEMacAddress bleMacAddress(addr->a.val); + HTDBG((std::string)bleMacAddress); + + ConnectedDeviceState& state = findOrCreateStateByConnection(conn, true); + auto device = db.device(bleMacAddress); // Find by actual current physical address + + if (err) { // 2 = SMP issues? StreetPass blocker on Android device perhaps. Disabled SMP use? + // When connecting to some devices (E.g. HTC Vive base station), you will connect BUT get an error code + // The below ensures that this is counted as a connection failure + + HTDBG("Connected: Error value:-"); + HTDBG(std::to_string(err)); + // Note: See Bluetooth Specification, Vol 2. Part D (Error codes) + + bt_conn_unref(conn); + + state.state = BLEDeviceState::disconnected; + state.connection = NULL; + + // Log last disconnected time in BLE database + device->state(BLEDeviceState::disconnected); + + // if (targetForConnection.has_value() && connCallback.has_value()) { + // connCallback.value()(targetForConnection.value(),false); + // } + return; + } + + state.connection = conn; + bt_addr_le_copy(&state.address,addr); + state.state = BLEDeviceState::connected; + + // Log last connected time in BLE database + device->state(BLEDeviceState::connected); + + + // if (targetForConnection.has_value() && connCallback.has_value()) { + // connCallback.value()(targetForConnection.value(),true); + // } + + } + + void disconnected(struct bt_conn *conn, uint8_t reason) override + { + HTDBG("********** Zephyr disconnection callback. Mac of disconnected:"); + + auto addr = bt_conn_get_dst(conn); + char addr_str[BT_ADDR_LE_STR_LEN]; + bt_addr_le_to_str(addr, addr_str, sizeof(addr_str)); + std::string addrStr(addr_str); + BLEMacAddress bleMacAddress(addr->a.val); + HTDBG((std::string)bleMacAddress); + + if (reason) { + HTDBG("Disconnection: Reason value:-"); + HTDBG(std::to_string(reason)); + // Note: See Bluetooth Specification, Vol 2. Part D (Error codes) + // 0x20 = Unsupported LL parameter value + } + + // TODO log disconnection time in ble database + + bt_conn_unref(conn); + ConnectedDeviceState& state = findOrCreateStateByConnection(conn); + + state.state = BLEDeviceState::disconnected; + state.connection = NULL; + + // Log last disconnected time in BLE database + auto device = db.device(bleMacAddress); // Find by actual current physical address + device->state(BLEDeviceState::disconnected); + } + + void discovery_completed_cb(struct bt_gatt_dm *dm, void *context) override + { + HTDBG("The GATT discovery procedure succeeded"); + const struct bt_gatt_dm_attr *prev = NULL; + bool found = false; + ConnectedDeviceState& state = findOrCreateStateByConnection(bt_gatt_dm_conn_get(dm)); + auto device = db.device(state.target); + do { + prev = bt_gatt_dm_char_next(dm,prev); + if (NULL != prev) { + // Check for match of uuid to a herald read payload char + struct bt_gatt_chrc *chrc = bt_gatt_dm_attr_chrc_val(prev); + + int matches = bt_uuid_cmp(chrc->uuid, &zephyrinternal::herald_char_payload_uuid.uuid); + if (0 == matches) { + HTDBG(" - FOUND Herald read characteristic. Reading."); + device->payloadCharacteristic(BLESensorConfiguration::payloadCharacteristicUUID); + // initialise payload data for this state + state.readPayload.clear(); + + // if match, for a read + found = true; + // set handles + + // TODO REFACTOR THE ACTUAL FETCHING OF PAYLOAD TO READPAYLOAD FUNCTION + // - Actually important, as currently a wearable will request the char multiple times from iOS before a reply is received + zephyrinternal::read_params.single.handle = chrc->value_handle; + zephyrinternal::read_params.single.offset = 0x0000; // gets changed on each use + int readErr = bt_gatt_read(bt_gatt_dm_conn_get(dm), &zephyrinternal::read_params); + if (readErr) { + HTDBG("GATT read error: TBD");//, readErr); + // bt_conn_disconnect(bt_gatt_dm_conn_get(dm), BT_HCI_ERR_REMOTE_USER_TERM_CONN); + } + + continue; // check for other characteristics too + } + matches = bt_uuid_cmp(chrc->uuid, &zephyrinternal::herald_char_signal_android_uuid.uuid); + if (0 == matches) { + HTDBG(" - FOUND Herald android signal characteristic. logging."); + device->signalCharacteristic(BLESensorConfiguration::androidSignalCharacteristicUUID); + device->operatingSystem(BLEDeviceOperatingSystem::android); + + continue; // check for other characteristics too + } + matches = bt_uuid_cmp(chrc->uuid, &zephyrinternal::herald_char_signal_ios_uuid.uuid); + if (0 == matches) { + HTDBG(" - FOUND Herald ios signal characteristic. logging."); + device->signalCharacteristic(BLESensorConfiguration::iosSignalCharacteristicUUID); + device->operatingSystem(BLEDeviceOperatingSystem::ios); + + continue; // check for other characteristics too + } + // otherwise + char uuid_str[32]; + bt_uuid_to_str(chrc->uuid,uuid_str,sizeof(uuid_str)); + HTDBG(" - Char doesn't match any herald char uuid:-"); //, log_strdup(uuid_str)); + HTDBG(uuid_str); + } + } while (NULL != prev); + + if (!found) { + HTDBG("Herald read payload char not found in herald service (weird...). Ignoring device."); + device->ignore(true); + // bt_conn_disconnect(bt_gatt_dm_conn_get(dm), BT_HCI_ERR_REMOTE_USER_TERM_CONN); + } + + // No it doesn't - this is safe: does ending this here break our bt_gatt_read? (as it uses that connection?) + int err = bt_gatt_dm_data_release(dm); + if (err) { + HTDBG("Could not release the discovery data, error code: TBD"); + // bt_conn_disconnect(bt_gatt_dm_conn_get(dm), BT_HCI_ERR_REMOTE_USER_TERM_CONN); + } + + // very last action - for concurrency reasons (C++17 threading/mutex/async/future not available on Zephyr) + std::vector serviceList; + serviceList.push_back(BLESensorConfiguration::serviceUUID); + device->services(serviceList); + } + + void discovery_service_not_found_cb(struct bt_conn *conn, void *context) override + { + HTDBG("The service could not be found during the discovery. Ignoring device:"); + ConnectedDeviceState& state = findOrCreateStateByConnection(conn); + HTDBG((std::string)state.target); + + auto device = db.device(state.target); + std::vector serviceList; // empty service list // TODO put other listened-for services here + device->services(serviceList); + device->ignore(true); + } + + void discovery_error_found_cb(struct bt_conn *conn, int err, void *context) override + { + HTDBG("The discovery procedure failed with "); + HTDBG(std::to_string(err)); + // TODO decide if we should ignore the device here, or just keep trying + } + + uint8_t gatt_read_cb(struct bt_conn *conn, uint8_t err, + struct bt_gatt_read_params *params, + const void *data, uint16_t length) override + { + // Fetch state for this element + ConnectedDeviceState& state = findOrCreateStateByConnection(conn); + if (NULL == data) { + HTDBG("Finished reading CHAR read payload:-"); + HTDBG(state.readPayload.hexEncodedString()); + + // Set final read payload (triggers success callback on observer) + db.device(state.target)->payloadData(state.readPayload); + + return 0; + } + + state.readPayload.append((const uint8_t*)data,0,length); + return length; + } + + // std::optional findState(const TargetIdentifier& forTarget); + // std::optional findStateByConnection(struct bt_conn *conn); + ConnectedDeviceState& findOrCreateState(const TargetIdentifier& forTarget) + { + auto iter = connectionStates.find(forTarget); + if (connectionStates.end() != iter) { + return iter->second; + } + return connectionStates.emplace(forTarget, forTarget).first->second; + // return connectionStates.find(forTarget)->second; + } + + ConnectedDeviceState& findOrCreateStateByConnection(struct bt_conn *conn, bool remoteInstigated = false) + { + for (auto& [key, value] : connectionStates) { + if (value.connection == conn) { + return value; + } + } + // Create target identifier from address + auto addr = bt_conn_get_dst(conn); + BLEMacAddress bleMacAddress(addr->a.val); + TargetIdentifier target((Data)bleMacAddress); + auto result = connectionStates.emplace(target, target); + bt_addr_le_copy(&result.first->second.address,addr); + result.first->second.remoteInstigated = remoteInstigated; + return result.first->second; + } + + void removeState(const TargetIdentifier& forTarget) + { + auto iter = connectionStates.find(forTarget); + if (connectionStates.end() != iter) { + connectionStates.erase(iter); + } + } + + // internal call methods + void startScanning() + { + if (isScanning) { + return; + } + int err = bt_le_scan_start(&zephyrinternal::defaultScanParam, &zephyrinternal::scan_cb); // scan_cb linked via BT_SCAN_CB_INIT call + + if (0 != err) { + HTDBG("Starting scanning failed"); + return; + } + isScanning = true; + } + + void stopScanning() + { + if (isScanning) { + isScanning = false; + bt_le_scan_stop(); + } + } + + void gatt_discover(struct bt_conn *conn) + { + HTDBG("Attempting GATT service discovery"); + int err; + + // begin introspection + err = bt_gatt_dm_start(conn, &zephyrinternal::herald_uuid.uuid, &zephyrinternal::discovery_cb, NULL); + if (err) { + HTDBG("could not start the discovery procedure, error code") + HTDBG(std::to_string(err)); + bt_conn_disconnect(conn, BT_HCI_ERR_REMOTE_USER_TERM_CONN); // ensures disconnect() called, and loop completed + return; + } + HTDBG("Service discovery succeeded... now do something with it in the callback!"); + } + + ContextT& m_context; + BluetoothStateManager& m_stateManager; + std::shared_ptr m_pds; + BLEDatabaseT& db; + + std::vector> delegates; + + std::map connectionStates; + bool isScanning; + + HLOGGER(ContextT); }; } diff --git a/herald/include/herald/ble/zephyr/concrete_ble_transmitter.h b/herald/include/herald/ble/zephyr/concrete_ble_transmitter.h index d41fad8..e6e6deb 100644 --- a/herald/include/herald/ble/zephyr/concrete_ble_transmitter.h +++ b/herald/include/herald/ble/zephyr/concrete_ble_transmitter.h @@ -9,7 +9,6 @@ #include "../ble_receiver.h" #include "../ble_sensor.h" #include "../ble_transmitter.h" -#include "../ble_concrete.h" #include "../ble_protocols.h" #include "../bluetooth_state_manager.h" #include "../ble_device_delegate.h" @@ -21,10 +20,22 @@ #include "../ble_coordinator.h" #include "../../datatype/bluetooth_state.h" +// nRF Connect SDK includes +#include +#include +#include +#include +#include +#include +#include +#include + +// C++17 includes #include #include #include #include +#include namespace herald { namespace ble { @@ -33,28 +44,216 @@ using namespace herald::datatype; using namespace herald::ble::filter; using namespace herald::payload; -// TODO zephyr internal functions called by template +static PayloadDataSupplier* latestPds = NULL; + +// zephyr internal functions called by template + +/* Herald Service Variables */ +static struct bt_uuid_128 herald_uuid = BT_UUID_INIT_128( + 0x9b, 0xfd, 0x5b, 0xd6, 0x72, 0x45, 0x1e, 0x80, 0xd3, 0x42, 0x46, 0x47, 0xaf, 0x32, 0x81, 0x42 +); +static struct bt_uuid_128 herald_char_signal_uuid = BT_UUID_INIT_128( + 0x11, 0x1a, 0x82, 0x80, 0x9a, 0xe0, 0x24, 0x83, 0x7a, 0x43, 0x2e, 0x09, 0x13, 0xb8, 0x17, 0xf6 +); +static struct bt_uuid_128 herald_char_payload_uuid = BT_UUID_INIT_128( + 0xe7, 0x33, 0x89, 0x8f, 0xe3, 0x43, 0x21, 0xa1, 0x29, 0x48, 0x05, 0x8f, 0xf8, 0xc0, 0x98, 0x3e +); + +namespace zephyrinternal { + static void get_tx_power(uint8_t handle_type, uint16_t handle, int8_t *tx_pwr_lvl); + + + static ssize_t read_vnd(struct bt_conn *conn, const struct bt_gatt_attr *attr, + void *buf, uint16_t len, uint16_t offset); + static ssize_t write_vnd(struct bt_conn *conn, const struct bt_gatt_attr *attr, + const void *buf, uint16_t len, uint16_t offset, + uint8_t flags); + static ssize_t read_payload(struct bt_conn *conn, const struct bt_gatt_attr *attr, + void *buf, uint16_t len, uint16_t offset); + static ssize_t write_payload(struct bt_conn *conn, const struct bt_gatt_attr *attr, + const void *buf, uint16_t len, uint16_t offset, + uint8_t flags); +} + +// Define kernel memory statically so we definitely have it +BT_GATT_SERVICE_DEFINE(herald_svc, + BT_GATT_PRIMARY_SERVICE(&herald_uuid), + BT_GATT_CHARACTERISTIC(&herald_char_signal_uuid.uuid, + BT_GATT_CHRC_WRITE, + BT_GATT_PERM_WRITE, + zephyrinternal::read_vnd,zephyrinternal::write_vnd, nullptr), + BT_GATT_CHARACTERISTIC(&herald_char_payload_uuid.uuid, + BT_GATT_CHRC_READ, + BT_GATT_PERM_READ, + zephyrinternal::read_payload, zephyrinternal::write_payload, nullptr) +); +static auto bp = BT_LE_ADV_CONN_NAME; // No TxPower support +/* +static auto bp = BT_LE_ADV_PARAM(BT_LE_ADV_OPT_CONNECTABLE | \ + BT_LE_ADV_OPT_USE_NAME, \ + BT_GAP_ADV_FAST_INT_MIN_2, \ + BT_GAP_ADV_FAST_INT_MAX_2, \ + BT_LE_ADV_OPT_USE_TX_POWER, \ + NULL); // txpower - REQUIRES EXT ADV OPT on Zephyr (experimental) +*/ +static struct bt_data ad[] = { + BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)), + // BT_DATA_BYTES(BT_DATA_TX_POWER, 0x00 ), // See https://github.com/vmware/herald-for-cpp/issues/26 + BT_DATA_BYTES(BT_DATA_UUID16_ALL, + BT_UUID_16_ENCODE(BT_UUID_DIS_VAL), + BT_UUID_16_ENCODE(BT_UUID_GATT_VAL), + BT_UUID_16_ENCODE(BT_UUID_GAP_VAL) + ), + BT_DATA_BYTES(BT_DATA_UUID128_ALL, + 0x9b, 0xfd, 0x5b, 0xd6, 0x72, 0x45, 0x1e, 0x80, + 0xd3, 0x42, 0x46, 0x47, 0xaf, 0x32, 0x81, 0x42 + ), +}; + -template -class ConcreteBLETransmitter : public BLETransmitter, public std::enable_shared_from_this> { + +template +class ConcreteBLETransmitter : public BLETransmitter { public: ConcreteBLETransmitter(ContextT& ctx, BluetoothStateManager& bluetoothStateManager, - std::shared_ptr payloadDataSupplier, std::shared_ptr bleDatabase); + std::shared_ptr payloadDataSupplier, BLEDatabaseT& bleDatabase) + : m_context(ctx), + m_stateManager(bluetoothStateManager), + m_pds(payloadDataSupplier), + m_db(bleDatabase), + delegates(), + isAdvertising(false) + + HLOGGERINIT(ctx,"Sensor","BLE.ConcreteBLETransmitter") + { + latestPds = m_pds.get(); + } + ConcreteBLETransmitter(const ConcreteBLETransmitter& from) = delete; ConcreteBLETransmitter(ConcreteBLETransmitter&& from) = delete; - ~ConcreteBLETransmitter(); + + ~ConcreteBLETransmitter() + { + stop(); + latestPds = NULL; + } // Coordination overrides - Since v1.2-beta3 - std::optional> coordinationProvider() override; + std::optional> coordinationProvider() override { + return {}; + } // Sensor overrides - void add(const std::shared_ptr& delegate) override; - void start() override; - void stop() override; + void add(const std::shared_ptr& delegate) override { + delegates.push_back(delegate); + } + + void start() override { + HTDBG("ConcreteBLETransmitter::start"); + if (!BLESensorConfiguration::advertisingEnabled) { + HTDBG("Sensor Configuration has advertising disabled. Returning."); + return; + } + m_context.getPlatform().getAdvertiser().registerStopCallback([this] () -> void { + stopAdvertising(); + }); + m_context.getPlatform().getAdvertiser().registerStartCallback([this] () -> void { + startAdvertising(); + }); + HTDBG("Advertising callbacks registered"); + + // Ensure our zephyr context has bluetooth ready + m_context.getPlatform().startBluetooth(); + + HTDBG("Bluetooth started. Requesting start of adverts"); + + startAdvertising(); + } + + void stop() override { + HTDBG("ConcreteBLETransmitter::stop"); + if (!BLESensorConfiguration::advertisingEnabled) { + HTDBG("Sensor Configuration has advertising disabled. Returning."); + return; + } + stopAdvertising(); + } private: - class Impl; - std::shared_ptr mImpl; // shared to allow static callbacks to be bound + ContextT& m_context; + BluetoothStateManager& m_stateManager; + std::shared_ptr m_pds; + BLEDatabaseT& m_db; + + std::vector> delegates; + + bool isAdvertising; + + HLOGGER(ContextT); + + // Internal methods + void startAdvertising() + { + // HTDBG("startAdvertising called"); + if (!BLESensorConfiguration::advertisingEnabled) { + HTDBG("Sensor Configuration has advertising disabled. Returning."); + return; + } + if (isAdvertising) { + // HTDBG("Already advertising. Returning."); + return; + } + + // Note: TxPower currently disabled due to restricted advert space and the need to include Herald's service. + // See https://github.com/vmware/herald-for-cpp/issues/26 + // Get current TxPower and alter advert accordingly:- + // int8_t txp_get = 0; + // zephyrinternal::get_tx_power(BT_HCI_VS_LL_HANDLE_TYPE_ADV,0, &txp_get); + // HTDBG("Zephyr tx power:-"); + // HTDBG(std::to_string(txp_get)); + // herald::ble::ad[1] = bt_data{ + // .type=BT_DATA_TX_POWER, + // .data_len=sizeof(txp_get), + // .data=(const uint8_t *)uint8_t(txp_get) + // }; + + // Now start advertising + // See https://developer.nordicsemi.com/nRF_Connect_SDK/doc/latest/zephyr/reference/bluetooth/gap.html#group__bt__gap_1gac45d16bfe21c3c38e834c293e5ebc42b + int success = bt_le_adv_start(herald::ble::bp, herald::ble::ad, ARRAY_SIZE(herald::ble::ad), NULL, 0); + if (0 != success) { + HTDBG("Start advertising failed"); + return; + } + + // zephyrinternal::get_tx_power(BT_HCI_VS_LL_HANDLE_TYPE_ADV,0, &txp_get); + // HTDBG("Zephyr tx power post advertising starting:-"); + // HTDBG(std::to_string(txp_get)); + + HTDBG("Start advertising completed successfully"); + isAdvertising = true; + } + + void stopAdvertising() + { + // HTDBG("stopAdvertising called"); + if (!BLESensorConfiguration::advertisingEnabled) { + HTDBG("Sensor Configuration has advertising disabled. Returning."); + return; + } + if (!isAdvertising) { + // HTDBG("Not advertising already. Returning."); + return; + } + isAdvertising = false; + int success = bt_le_adv_stop(); + if (0 != success) { + HTDBG("Stop advertising failed"); + return; + } + // Don't stop Bluetooth altogether - this is done by the ZephyrContext->stopBluetooth() function only + HTDBG("Stop advertising completed successfully"); + } + }; } diff --git a/herald/include/herald/context.h b/herald/include/herald/context.h index 06abfe5..2bbdcf5 100644 --- a/herald/include/herald/context.h +++ b/herald/include/herald/context.h @@ -47,16 +47,17 @@ namespace herald { /// Covers all cross-cutting concerns methods and helpers to prevent tight coupling between components /// Currently hard-coded to include Bluetooth relevant radio, but this should be abstracted in future to /// compile out if Bluetooth support is not needed -template struct Context { using logging_sink_type = LoggingSinkT; - Context(LoggingSinkT& sink,BluetoothStateManagerT& bsm) noexcept - : loggingSink(sink), bleStateManager(bsm) {} + Context(PlatformT& platform,LoggingSinkT& sink,BluetoothStateManagerT& bsm) noexcept + : platform(platform), loggingSink(sink), bleStateManager(bsm) {} Context(const Context& other) noexcept - : loggingSink(other.loggingSink), bleStateManager(other.bleStateManager) + : platform(other.platform), loggingSink(other.loggingSink), bleStateManager(other.bleStateManager) {} // Context(Context&& other) // : loggingSink(std::move(other.loggingSink)), bleStateManager(std::move(other.bleStateManager)) @@ -64,7 +65,8 @@ struct Context { ~Context() = default; - Context& operator=(Context& other) noexcept { + Context& operator=(Context& other) noexcept { + platform = other.platform; loggingSink = other.loggingSink; bleStateManager = other.bleStateManager; return *this; @@ -80,11 +82,20 @@ struct Context { return bleStateManager; } + // \brief Returns platform-specific implementation. E.g. Zephyr specific calls + PlatformT& getPlatform() { + return platform; + } + private: + PlatformT& platform; LoggingSinkT& loggingSink; BluetoothStateManagerT& bleStateManager; }; +// \brief Default empty platform type for platforms that have no custom functionality +struct DefaultPlatformType {}; + } // end namespace #endif \ No newline at end of file diff --git a/herald/include/herald/data/devnull_logging_sink.h b/herald/include/herald/data/devnull_logging_sink.h new file mode 100644 index 0000000..71b5bae --- /dev/null +++ b/herald/include/herald/data/devnull_logging_sink.h @@ -0,0 +1,21 @@ +// Copyright 2020-2021 Herald Project Contributors +// SPDX-License-Identifier: Apache-2.0 +// + +#ifndef HERALD_DEVNULL_LOGGING_SINK +#define HERALD_DEVNULL_LOGGING_SINK + +#include "herald/data/sensor_logger.h" + +namespace herald::data { + +struct DevNullLoggingSink { + DevNullLoggingSink(); + ~DevNullLoggingSink(); + + void log(const std::string& subsystem, const std::string& category, SensorLoggerLevel level, std::string message); +}; + +} + +#endif \ No newline at end of file diff --git a/herald/include/herald/zephyr_context.h b/herald/include/herald/zephyr_context.h index b24179b..a4db221 100644 --- a/herald/include/herald/zephyr_context.h +++ b/herald/include/herald/zephyr_context.h @@ -50,7 +50,9 @@ namespace zephyrinternal { /// /// \brief Holds generic state across our application for any Zephyr RTOS device. /// -/// Provides a solid class that holds information and types to be pass to Context +/// Provides a solid class that holds information and types to be pass to Context. +/// +/// Also acts as the Zephyr PlatformT in Context. /// class ZephyrContextProvider : BluetoothStateManager { public: @@ -61,7 +63,7 @@ class ZephyrContextProvider : BluetoothStateManager { BluetoothStateManager& getBluetoothStateManager(); // Bluetooth State Manager override methods - void add(std::shared_ptr delegate) override; + void add(BluetoothStateManagerDelegate& delegate) override; BluetoothState state() override; // Zephyr OS specific methods @@ -79,7 +81,7 @@ class ZephyrContextProvider : BluetoothStateManager { zephyrinternal::Advertiser advertiser; - std::vector> stateDelegates; + std::vector> stateDelegates; bool bluetoothEnabled; }; diff --git a/herald/src/ble/zephyr/concrete_ble_receiver.cpp b/herald/src/ble/zephyr/concrete_ble_receiver.cpp index c0049ac..ff291dc 100644 --- a/herald/src/ble/zephyr/concrete_ble_receiver.cpp +++ b/herald/src/ble/zephyr/concrete_ble_receiver.cpp @@ -2,1235 +2,11 @@ // SPDX-License-Identifier: Apache-2.0 // -#include "herald/zephyr_context.h" -#include "herald/data/sensor_logger.h" #include "herald/ble/zephyr/concrete_ble_receiver.h" -#include "herald/ble/ble_concrete.h" -#include "herald/ble/ble_database.h" -#include "herald/ble/ble_receiver.h" -#include "herald/ble/ble_sensor.h" -#include "herald/ble/ble_sensor_configuration.h" -#include "herald/ble/bluetooth_state_manager.h" -#include "herald/datatype/data.h" -#include "herald/datatype/payload_data.h" -#include "herald/datatype/immediate_send_data.h" -#include "herald/ble/ble_mac_address.h" - -// nRF Connect SDK includes -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -// C++17 includes -#include -#include -#include namespace herald { namespace ble { -using namespace herald::datatype; -using namespace herald::data; - -// ZEPHYR UTILITY FUNCTIONS -/** wait with timeout for Zephyr. Returns true if the function timed out rather than completed **/ -uint32_t waitWithTimeout(uint32_t timeoutMillis, k_timeout_t period, std::function keepWaiting) -{ - uint32_t start_time; - uint32_t stop_time; - uint32_t millis_spent; - bool notComplete = keepWaiting(); - if (!notComplete) { - return 0; - } - /* capture initial time stamp */ - start_time = k_uptime_get_32(); - - /* capture final time stamp */ - stop_time = k_uptime_get_32(); - /* compute how long the work took (assumes no counter rollover) */ - millis_spent = stop_time - start_time; - - while (millis_spent < timeoutMillis && notComplete) { - k_sleep(period); - notComplete = keepWaiting(); - - /* capture final time stamp */ - stop_time = k_uptime_get_32(); - /* compute how long the work took (assumes no counter rollover) */ - millis_spent = stop_time - start_time; - } - if (notComplete) { - return millis_spent; - } else { - return 0; - } -} - - -struct ConnectedDeviceState { - ConnectedDeviceState(const TargetIdentifier& id) - : target(id), state(BLEDeviceState::disconnected), connection(NULL), address(), - readPayload(), immediateSend(), remoteInstigated(false) - {} - ConnectedDeviceState(const ConnectedDeviceState& from) = delete; - ConnectedDeviceState(ConnectedDeviceState&& from) = delete; - ~ConnectedDeviceState() = default; - - TargetIdentifier target; - BLEDeviceState state; - bt_conn* connection; - bt_addr_le_t address; - PayloadData readPayload; - ImmediateSendData immediateSend; - bool remoteInstigated; -}; - - -// struct AddrRef { -// const bt_addr_le_t* addr; -// }; - -namespace zephyrinternal { - - /* Herald Service Variables */ - static struct bt_uuid_128 herald_uuid = BT_UUID_INIT_128( - 0x9b, 0xfd, 0x5b, 0xd6, 0x72, 0x45, 0x1e, 0x80, 0xd3, 0x42, 0x46, 0x47, 0xaf, 0x32, 0x81, 0x42 - ); - static struct bt_uuid_128 herald_char_signal_android_uuid = BT_UUID_INIT_128( - 0x11, 0x1a, 0x82, 0x80, 0x9a, 0xe0, 0x24, 0x83, 0x7a, 0x43, 0x2e, 0x09, 0x13, 0xb8, 0x17, 0xf6 - ); - static struct bt_uuid_128 herald_char_signal_ios_uuid = BT_UUID_INIT_128( - 0x63, 0x43, 0x2d, 0xb0, 0xad, 0xa4, 0xf3, 0x8a, 0x9a, 0x4a, 0xe4, 0xea, 0xf2, 0xd5, 0xb0, 0x0e - ); - static struct bt_uuid_128 herald_char_payload_uuid = BT_UUID_INIT_128( - 0xe7, 0x33, 0x89, 0x8f, 0xe3, 0x43, 0x21, 0xa1, 0x29, 0x48, 0x05, 0x8f, 0xf8, 0xc0, 0x98, 0x3e - ); - - - bt_le_conn_param* BTLEConnParam = BT_LE_CONN_PARAM_DEFAULT; // BT_LE_CONN_PARAM(0x018,3200,0,400); // NOT BT_LE_CONN_PARAM_DEFAULT; - bt_conn_le_create_param* BTLECreateParam = BT_CONN_LE_CREATE_CONN; // BT_CONN_LE_CREATE_PARAM(BT_CONN_LE_OPT_NONE, 0x0010,0x0010);// NOT BT_CONN_LE_CREATE_CONN; - - static struct bt_conn_le_create_param defaultCreateParam = BT_CONN_LE_CREATE_PARAM_INIT( - BT_CONN_LE_OPT_NONE, BT_GAP_SCAN_FAST_INTERVAL, BT_GAP_SCAN_FAST_INTERVAL - ); - static struct bt_le_conn_param defaultConnParam = BT_LE_CONN_PARAM_INIT( - //BT_GAP_INIT_CONN_INT_MIN, BT_GAP_INIT_CONN_INT_MAX, 0, 400 - //12, 12 // aka 15ms, default from apple documentation - 0x50, 0x50, // aka 80ms, from nRF SDK LLPM sample - 0, 400 - ); - // Note for apple see: https://developer.apple.com/library/archive/qa/qa1931/_index.html - // And https://developer.apple.com/accessories/Accessory-Design-Guidelines.pdf (BLE section) - - static struct bt_le_scan_param defaultScanParam = //BT_LE_SCAN_PASSIVE; - { - .type = BT_LE_SCAN_TYPE_PASSIVE, // passive scan - .options = BT_LE_SCAN_OPT_FILTER_DUPLICATE, // Scans for EVERYTHING - .interval = BT_GAP_SCAN_FAST_INTERVAL, // 0x0010, // V.FAST, NOT BT_GAP_SCAN_FAST_INTERVAL - gap.h - .window = BT_GAP_SCAN_FAST_WINDOW // 0x0010, // V.FAST, NOT BT_GAP_SCAN_FAST_INTERVAL - gap.h - }; - - /** - * Why is this necessary? Traditional pointer-to-function cannot easily - * and reliably be wrapped with std::function/bind/mem_fn. We also need - * the Herald API to use subclasses for each platform, necessitating - * some sort of static bridge. Not pretty, but works and allows us to - * prevent nullptr problems - */ - std::optional> - concreteReceiverInstance; - - - // static struct bt_conn* conn = NULL; - - // NOTE: The below is called multiple times for ONE char value. Keep appending to result until NULL==data. - static uint8_t gatt_read_cb(struct bt_conn *conn, uint8_t err, - struct bt_gatt_read_params *params, - const void *data, uint16_t length) - { - if (concreteReceiverInstance.has_value()) { - return concreteReceiverInstance.value()->gatt_read_cb(conn,err,params,data,length); - } - return length; // say we've consumed the data anyway - } - - static struct bt_gatt_read_params read_params = { - .func = gatt_read_cb, - .handle_count = 1, - .single = { - .handle = 0x0000, - .offset = 0x0000 - } - }; - - - // void scan_init(void) - // { - // // int err; - - // // struct bt_scan_init_param scan_init = { - // // .connect_if_match = 0, // no auto connect (handled by herald protocol coordinator) - // // .scan_param = NULL, - // // .conn_param = BT_LE_CONN_PARAM_DEFAULT - // // }; - - // // bt_scan_init(&scan_init); - // // bt_scan_cb_register(&scan_cb); - - // /* - // err = bt_scan_filter_add(BT_SCAN_FILTER_TYPE_UUID, herald_uuid); - // if (err) { - // printk("Scanning filters cannot be set (err %d)\n", err); - - // return; - // } - - // err = bt_scan_filter_enable(BT_SCAN_UUID_FILTER, false); - // if (err) { - // printk("Filters cannot be turned on (err %d)\n", err); - // } - // */ - // } - - - static void connected(struct bt_conn *conn, uint8_t err) - { - if (concreteReceiverInstance.has_value()) { - concreteReceiverInstance.value()->connected(conn,err); - } - } - - static void disconnected(struct bt_conn *conn, uint8_t reason) - { - if (concreteReceiverInstance.has_value()) { - concreteReceiverInstance.value()->disconnected(conn,reason); - } - } - - static void le_param_updated(struct bt_conn *conn, uint16_t interval, - uint16_t latency, uint16_t timeout) - { - if (concreteReceiverInstance.has_value()) { - concreteReceiverInstance.value()->le_param_updated(conn,interval,latency,timeout); - } - } - - static struct bt_conn_cb conn_callbacks = { - .connected = connected, - .disconnected = disconnected, - .le_param_updated = le_param_updated, - }; - - // static bt_addr_le_t *last_addr = BT_ADDR_LE_NONE; - - void scan_cb(const bt_addr_le_t *addr, int8_t rssi, uint8_t adv_type, - struct net_buf_simple *buf) { - if (concreteReceiverInstance.has_value()) { - concreteReceiverInstance.value()->scan_cb(addr,rssi,adv_type,buf); - } - } - - // BT_SCAN_CB_INIT(scan_cbs, scan_filter_match, ); - - static struct bt_scan_init_param scan_init = { - .scan_param = &defaultScanParam, - .connect_if_match = false, - .conn_param = &defaultConnParam - }; - - // void scan_filter_match(struct bt_scan_device_info *device_info, - // struct bt_scan_filter_match *filter_match, - // bool connectable) - // { - // char addr[BT_ADDR_LE_STR_LEN]; - - // bt_addr_le_to_str(device_info->recv_info->addr, addr, sizeof(addr)); - - // printk("Filters matched. Address: %s connectable: %s\n", - // addr, connectable ? "yes" : "no"); - // } - - // void scan_connecting_error(struct bt_scan_device_info *device_info) - // { - // printk("Connecting failed\n"); - // } - - // void scan_connecting(struct bt_scan_device_info *device_info, - // struct bt_conn *conn) - // { - // //default_conn = bt_conn_ref(conn); - // } - - // void scan_filter_no_match(struct bt_scan_device_info *device_info, - // bool connectable) - // { - // int err; - // struct bt_conn *conn; - // char addr[BT_ADDR_LE_STR_LEN]; - - // if (device_info->recv_info->adv_type == BT_GAP_ADV_TYPE_ADV_DIRECT_IND) { - // bt_addr_le_to_str(device_info->recv_info->addr, addr, - // sizeof(addr)); - // printk("Direct advertising received from %s\n", addr); - // bt_scan_stop(); - - // err = bt_conn_le_create(device_info->recv_info->addr, - // BT_CONN_LE_CREATE_CONN, - // device_info->conn_param, &conn); - - // if (!err) { - // default_conn = bt_conn_ref(conn); - // bt_conn_unref(conn); - // } - // } - // } - - // BT_SCAN_CB_INIT(scan_cb, scan_filter_match, scan_filter_no_match, - // scan_connecting_error, scan_connecting); - - - - // GATT DISCOVERY INTERNAL METHODS - - static void discovery_completed_cb(struct bt_gatt_dm *dm, - void *context) - { - if (concreteReceiverInstance.has_value()) { - concreteReceiverInstance.value()->discovery_completed_cb(dm,context); - } - } - - static void discovery_service_not_found_cb(struct bt_conn *conn, - void *context) - { - if (concreteReceiverInstance.has_value()) { - concreteReceiverInstance.value()->discovery_service_not_found_cb(conn,context); - } - } - - static void discovery_error_found_cb(struct bt_conn *conn, - int err, - void *context) - { - if (concreteReceiverInstance.has_value()) { - concreteReceiverInstance.value()->discovery_error_found_cb(conn,err,context); - } - } - - static const struct bt_gatt_dm_cb discovery_cb = { - .completed = discovery_completed_cb, - .service_not_found = discovery_service_not_found_cb, - .error_found = discovery_error_found_cb, - }; - -} - - -template -class ConcreteBLEReceiver::Impl : public herald::zephyrinternal::Callbacks { -public: - Impl(ContextT& ctx, BluetoothStateManager& bluetoothStateManager, - std::shared_ptr payloadDataSupplier, - std::shared_ptr bleDatabase); - ~Impl(); - - // Zephyr OS callbacks - void scan_cb(const bt_addr_le_t *addr, int8_t rssi, uint8_t adv_type, - struct net_buf_simple *buf) override; - - void le_param_updated(struct bt_conn *conn, uint16_t interval, - uint16_t latency, uint16_t timeout) override; - void connected(struct bt_conn *conn, uint8_t err) override; - void disconnected(struct bt_conn *conn, uint8_t reason) override; - - void discovery_completed_cb(struct bt_gatt_dm *dm, void *context) override; - void discovery_service_not_found_cb(struct bt_conn *conn, void *context) override; - void discovery_error_found_cb(struct bt_conn *conn, int err, void *context) override; - - uint8_t gatt_read_cb(struct bt_conn *conn, uint8_t err, - struct bt_gatt_read_params *params, - const void *data, uint16_t length) override; - - // std::optional findState(const TargetIdentifier& forTarget); - // std::optional findStateByConnection(struct bt_conn *conn); - ConnectedDeviceState& findOrCreateState(const TargetIdentifier& toTarget); - ConnectedDeviceState& findOrCreateStateByConnection(struct bt_conn *conn, bool remoteInstigated = false); - void removeState(const TargetIdentifier& forTarget); - - // internal call methods - void startScanning(); - void stopScanning(); - void gatt_discover(struct bt_conn *conn); - - ContextT& m_context; - BluetoothStateManager& m_stateManager; - std::shared_ptr m_pds; - std::shared_ptr db; - - std::vector> delegates; - - std::map connectionStates; - bool isScanning; - - HLOGGER(ContextT); -}; - -template -ConcreteBLEReceiver::Impl::Impl(ContextT& ctx, BluetoothStateManager& bluetoothStateManager, - std::shared_ptr payloadDataSupplier, - std::shared_ptr bleDatabase) - : m_context(ctx), // Herald API guarantees this to be safe - m_stateManager(bluetoothStateManager), - m_pds(payloadDataSupplier), - db(bleDatabase), - delegates(), - connectionStates(), - isScanning(false) - HLOGGERINIT(ctx,"Sensor","BLE.ConcreteBLEReceiver") -{ - ; -} - -template -ConcreteBLEReceiver::Impl::~Impl() -{ - ; -} - -// NOTE: Optional references currently illegal in C++17 (Would need Boost) - -// std::optional -// ConcreteBLEReceiver::Impl::findState(const TargetIdentifier& forTarget) -// { -// auto iter = connectionStates.find(forTarget); -// if (connectionStates.end() != iter) { -// return iter->second; -// } -// return {}; -// } - -// std::optional -// ConcreteBLEReceiver::Impl::findStateByConnection(struct bt_conn *conn) -// { -// for (const auto& [key, value] : connectionStates) { -// if (value.connection == conn) { -// return value; -// } -// } -// return {}; -// } - -template -ConnectedDeviceState& -ConcreteBLEReceiver::Impl::findOrCreateState(const TargetIdentifier& forTarget) -{ - auto iter = connectionStates.find(forTarget); - if (connectionStates.end() != iter) { - return iter->second; - } - return connectionStates.emplace(forTarget, forTarget).first->second; - // return connectionStates.find(forTarget)->second; -} - -template -ConnectedDeviceState& -ConcreteBLEReceiver::Impl::findOrCreateStateByConnection(struct bt_conn *conn, bool remoteInstigated) -{ - for (auto& [key, value] : connectionStates) { - if (value.connection == conn) { - return value; - } - } - // Create target identifier from address - auto addr = bt_conn_get_dst(conn); - BLEMacAddress bleMacAddress(addr->a.val); - TargetIdentifier target((Data)bleMacAddress); - auto result = connectionStates.emplace(target, target); - bt_addr_le_copy(&result.first->second.address,addr); - result.first->second.remoteInstigated = remoteInstigated; - return result.first->second; -} - -template -void -ConcreteBLEReceiver::Impl::removeState(const TargetIdentifier& forTarget) -{ - auto iter = connectionStates.find(forTarget); - if (connectionStates.end() != iter) { - connectionStates.erase(iter); - } -} - -template -void -ConcreteBLEReceiver::Impl::stopScanning() -{ - if (isScanning) { - isScanning = false; - bt_le_scan_stop(); - } -} - -template -void -ConcreteBLEReceiver::Impl::startScanning() -{ - if (isScanning) { - return; - } - int err = bt_le_scan_start(&zephyrinternal::defaultScanParam, &zephyrinternal::scan_cb); // scan_cb linked via BT_SCAN_CB_INIT call - - if (0 != err) { - HTDBG("Starting scanning failed"); - return; - } - isScanning = true; -} - -template -void -ConcreteBLEReceiver::Impl::scan_cb(const bt_addr_le_t *addr, int8_t rssi, uint8_t adv_type, - struct net_buf_simple *buf) -{ - // identify device by both MAC and potential pseudoDeviceAddress - BLEMacAddress bleMacAddress(addr->a.val); - Data advert(buf->data,buf->len); - auto device = db->device(bleMacAddress,advert); - - // auto device = db->device(target); - if (device->ignore()) { - // device->rssi(RSSI(rssi)); // TODO should we do this so our update date works and shows this as a 'live' device? - return; - } - - // // Now pass to relevant BLEDatabase API call - if (!device->rssi().has_value()) { - char addr_str[BT_ADDR_LE_STR_LEN]; - bt_addr_le_to_str(addr, addr_str, sizeof(addr_str)); - std::string addrStr(addr_str); - HTDBG("New address FROM SCAN:-"); - HTDBG(addr_str); - } - - // Add this RSSI reading - called at the end to ensure all other data variables set - device->rssi(RSSI(rssi)); -} - -template -void -ConcreteBLEReceiver::Impl::gatt_discover(struct bt_conn *conn) -{ - HTDBG("Attempting GATT service discovery"); - int err; - - // begin introspection - err = bt_gatt_dm_start(conn, &zephyrinternal::herald_uuid.uuid, &zephyrinternal::discovery_cb, NULL); - if (err) { - HTDBG("could not start the discovery procedure, error code") - HTDBG(std::to_string(err)); - bt_conn_disconnect(conn, BT_HCI_ERR_REMOTE_USER_TERM_CONN); // ensures disconnect() called, and loop completed - return; - } - HTDBG("Service discovery succeeded... now do something with it in the callback!"); -} - -template -void -ConcreteBLEReceiver::Impl::le_param_updated(struct bt_conn *conn, uint16_t interval, - uint16_t latency, uint16_t timeout) -{ - HTDBG("le param updated called"); -} - -template -void -ConcreteBLEReceiver::Impl::connected(struct bt_conn *conn, uint8_t err) -{ - HTDBG("**************** Zephyr connection callback. Mac of connected:"); - - auto addr = bt_conn_get_dst(conn); - char addr_str[BT_ADDR_LE_STR_LEN]; - bt_addr_le_to_str(addr, addr_str, sizeof(addr_str)); - std::string addrStr(addr_str); - BLEMacAddress bleMacAddress(addr->a.val); - HTDBG((std::string)bleMacAddress); - - ConnectedDeviceState& state = findOrCreateStateByConnection(conn, true); - auto device = db->device(bleMacAddress); // Find by actual current physical address - - if (err) { // 2 = SMP issues? StreetPass blocker on Android device perhaps. Disabled SMP use? - // When connecting to some devices (E.g. HTC Vive base station), you will connect BUT get an error code - // The below ensures that this is counted as a connection failure - - HTDBG("Connected: Error value:-"); - HTDBG(std::to_string(err)); - // Note: See Bluetooth Specification, Vol 2. Part D (Error codes) - - bt_conn_unref(conn); - - state.state = BLEDeviceState::disconnected; - state.connection = NULL; - - // Log last disconnected time in BLE database - device->state(BLEDeviceState::disconnected); - - // if (targetForConnection.has_value() && connCallback.has_value()) { - // connCallback.value()(targetForConnection.value(),false); - // } - return; - } - - state.connection = conn; - bt_addr_le_copy(&state.address,addr); - state.state = BLEDeviceState::connected; - - // Log last connected time in BLE database - device->state(BLEDeviceState::connected); - - - // if (targetForConnection.has_value() && connCallback.has_value()) { - // connCallback.value()(targetForConnection.value(),true); - // } - -} - -template -void -ConcreteBLEReceiver::Impl::disconnected(struct bt_conn *conn, uint8_t reason) -{ - HTDBG("********** Zephyr disconnection callback. Mac of disconnected:"); - - auto addr = bt_conn_get_dst(conn); - char addr_str[BT_ADDR_LE_STR_LEN]; - bt_addr_le_to_str(addr, addr_str, sizeof(addr_str)); - std::string addrStr(addr_str); - BLEMacAddress bleMacAddress(addr->a.val); - HTDBG((std::string)bleMacAddress); - - if (reason) { - HTDBG("Disconnection: Reason value:-"); - HTDBG(std::to_string(reason)); - // Note: See Bluetooth Specification, Vol 2. Part D (Error codes) - // 0x20 = Unsupported LL parameter value - } - - // TODO log disconnection time in ble database - - bt_conn_unref(conn); - ConnectedDeviceState& state = findOrCreateStateByConnection(conn); - - state.state = BLEDeviceState::disconnected; - state.connection = NULL; - - // Log last disconnected time in BLE database - auto device = db->device(bleMacAddress); // Find by actual current physical address - device->state(BLEDeviceState::disconnected); -} - -// Discovery callbacks - -template -void -ConcreteBLEReceiver::Impl::discovery_completed_cb(struct bt_gatt_dm *dm, - void *context) -{ - HTDBG("The GATT discovery procedure succeeded"); - const struct bt_gatt_dm_attr *prev = NULL; - bool found = false; - ConnectedDeviceState& state = findOrCreateStateByConnection(bt_gatt_dm_conn_get(dm)); - auto device = db->device(state.target); - do { - prev = bt_gatt_dm_char_next(dm,prev); - if (NULL != prev) { - // Check for match of uuid to a herald read payload char - struct bt_gatt_chrc *chrc = bt_gatt_dm_attr_chrc_val(prev); - - int matches = bt_uuid_cmp(chrc->uuid, &zephyrinternal::herald_char_payload_uuid.uuid); - if (0 == matches) { - HTDBG(" - FOUND Herald read characteristic. Reading."); - device->payloadCharacteristic(BLESensorConfiguration::payloadCharacteristicUUID); - // initialise payload data for this state - state.readPayload.clear(); - - // if match, for a read - found = true; - // set handles - - // TODO REFACTOR THE ACTUAL FETCHING OF PAYLOAD TO READPAYLOAD FUNCTION - // - Actually important, as currently a wearable will request the char multiple times from iOS before a reply is received - zephyrinternal::read_params.single.handle = chrc->value_handle; - zephyrinternal::read_params.single.offset = 0x0000; // gets changed on each use - int readErr = bt_gatt_read(bt_gatt_dm_conn_get(dm), &zephyrinternal::read_params); - if (readErr) { - HTDBG("GATT read error: TBD");//, readErr); - // bt_conn_disconnect(bt_gatt_dm_conn_get(dm), BT_HCI_ERR_REMOTE_USER_TERM_CONN); - } - - continue; // check for other characteristics too - } - matches = bt_uuid_cmp(chrc->uuid, &zephyrinternal::herald_char_signal_android_uuid.uuid); - if (0 == matches) { - HTDBG(" - FOUND Herald android signal characteristic. logging."); - device->signalCharacteristic(BLESensorConfiguration::androidSignalCharacteristicUUID); - device->operatingSystem(BLEDeviceOperatingSystem::android); - - continue; // check for other characteristics too - } - matches = bt_uuid_cmp(chrc->uuid, &zephyrinternal::herald_char_signal_ios_uuid.uuid); - if (0 == matches) { - HTDBG(" - FOUND Herald ios signal characteristic. logging."); - device->signalCharacteristic(BLESensorConfiguration::iosSignalCharacteristicUUID); - device->operatingSystem(BLEDeviceOperatingSystem::ios); - - continue; // check for other characteristics too - } - // otherwise - char uuid_str[32]; - bt_uuid_to_str(chrc->uuid,uuid_str,sizeof(uuid_str)); - HTDBG(" - Char doesn't match any herald char uuid:-"); //, log_strdup(uuid_str)); - HTDBG(uuid_str); - } - } while (NULL != prev); - - if (!found) { - HTDBG("Herald read payload char not found in herald service (weird...). Ignoring device."); - device->ignore(true); - // bt_conn_disconnect(bt_gatt_dm_conn_get(dm), BT_HCI_ERR_REMOTE_USER_TERM_CONN); - } - - // No it doesn't - this is safe: does ending this here break our bt_gatt_read? (as it uses that connection?) - int err = bt_gatt_dm_data_release(dm); - if (err) { - HTDBG("Could not release the discovery data, error code: TBD"); - // bt_conn_disconnect(bt_gatt_dm_conn_get(dm), BT_HCI_ERR_REMOTE_USER_TERM_CONN); - } - - // very last action - for concurrency reasons (C++17 threading/mutex/async/future not available on Zephyr) - std::vector serviceList; - serviceList.push_back(BLESensorConfiguration::serviceUUID); - device->services(serviceList); -} - -template -void -ConcreteBLEReceiver::Impl::discovery_service_not_found_cb(struct bt_conn *conn, - void *context) -{ - HTDBG("The service could not be found during the discovery. Ignoring device:"); - ConnectedDeviceState& state = findOrCreateStateByConnection(conn); - HTDBG((std::string)state.target); - - auto device = db->device(state.target); - std::vector serviceList; // empty service list // TODO put other listened-for services here - device->services(serviceList); - device->ignore(true); -} - -template -void -ConcreteBLEReceiver::Impl::discovery_error_found_cb(struct bt_conn *conn, - int err, - void *context) -{ - HTDBG("The discovery procedure failed with "); - HTDBG(std::to_string(err)); - // TODO decide if we should ignore the device here, or just keep trying -} - -template -uint8_t -ConcreteBLEReceiver::Impl::gatt_read_cb(struct bt_conn *conn, uint8_t err, - struct bt_gatt_read_params *params, - const void *data, uint16_t length) -{ - // Fetch state for this element - ConnectedDeviceState& state = findOrCreateStateByConnection(conn); - if (NULL == data) { - HTDBG("Finished reading CHAR read payload:-"); - HTDBG(state.readPayload.hexEncodedString()); - - // Set final read payload (triggers success callback on observer) - db->device(state.target)->payloadData(state.readPayload); - - return 0; - } - - state.readPayload.append((const uint8_t*)data,0,length); - return length; -} - - - - - - - - -template -ConcreteBLEReceiver::ConcreteBLEReceiver(ContextT& ctx, BluetoothStateManager& bluetoothStateManager, - std::shared_ptr payloadDataSupplier, std::shared_ptr bleDatabase) - : mImpl(std::make_shared(ctx,bluetoothStateManager,payloadDataSupplier,bleDatabase)) -{ - ; -} - -template -ConcreteBLEReceiver::~ConcreteBLEReceiver() -{ - ; -} - -template -std::optional> -ConcreteBLEReceiver::coordinationProvider() -{ - return {}; // we don't provide this, ConcreteBLESensor provides this. We provide HeraldV1ProtocolProvider -} - -template -void -ConcreteBLEReceiver::add(const std::shared_ptr& delegate) -{ - mImpl->delegates.push_back(delegate); -} - -template -void -ConcreteBLEReceiver::start() -{ - HDBG("ConcreteBLEReceiver::start"); - if (!BLESensorConfiguration::scanningEnabled) { - HDBG("Sensor Configuration has scanning disabled. Returning."); - return; - } - herald::ble::zephyrinternal::concreteReceiverInstance = mImpl; - - - // Ensure our zephyr context has bluetooth ready - HDBG("calling start bluetooth"); - int startOk = mImpl->m_context->startBluetooth(); - HDBG("start bluetooth done"); - if (0 != startOk) { - HDBG("ERROR starting context bluetooth:-"); - HDBG(std::to_string(startOk)); - } - - HDBG("Calling conn cb register"); - bt_conn_cb_register(&zephyrinternal::conn_callbacks); - HDBG("conn cb register done"); - - HDBG("calling bt scan start"); - mImpl->startScanning(); - - HDBG("ConcreteBLEReceiver::start completed successfully"); -} - -template -void -ConcreteBLEReceiver::stop() -{ - HDBG("ConcreteBLEReceiver::stop"); - if (!BLESensorConfiguration::scanningEnabled) { - HDBG("Sensor Configuration has scanning disabled. Returning."); - return; - } - - herald::ble::zephyrinternal::concreteReceiverInstance.reset(); // destroys the shared_ptr not necessarily the underlying value - - mImpl->stopScanning(); - - // Don't stop Bluetooth altogether - this is done by the ZephyrContext->stopBluetooth() function only - - HDBG("ConcreteBLEReceiver::stop completed successfully"); -} - - -template -bool -ConcreteBLEReceiver::immediateSend(Data data, const TargetIdentifier& targetIdentifier) -{ - return false; // TODO implement this -} - -template -bool -ConcreteBLEReceiver::immediateSendAll(Data data) -{ - return false; // TODO implement this -} - - -// Herald V1 Protocol Provider overrides - -// void -// ConcreteBLEReceiver::openConnection(const TargetIdentifier& toTarget, const HeraldConnectionCallback& connCallback) -// { -template -bool -ConcreteBLEReceiver::openConnection(const TargetIdentifier& toTarget) -{ - HDBG("openConnection"); - - // Create addr from TargetIdentifier data - ConnectedDeviceState& state = mImpl->findOrCreateState(toTarget); - uint8_t val[6] = {0,0,0,0,0,0}; - Data addrData = (Data)toTarget; // TODO change this to mac for target ID - uint8_t t; - bool cok = addrData.uint8(0,t); - if (cok) - val[0] = t; - cok = addrData.uint8(1,t); - if (cok) - val[1] = t; - cok = addrData.uint8(2,t); - if (cok) - val[2] = t; - cok = addrData.uint8(3,t); - if (cok) - val[3] = t; - cok = addrData.uint8(4,t); - if (cok) - val[4] = t; - cok = addrData.uint8(5,t); - if (cok) - val[5] = t; - // TODO create a convenience function in Data for the above - - // TODO don't assume RANDOM (1) in the below - bt_addr_le_t tempAddress{1, {{val[0],val[1],val[2],val[3],val[4],val[5]}}}; - // state.address = BT_ADDR_LE_NONE; - bt_addr_le_copy(&state.address, &tempAddress); - HDBG("Address copied. Constituted as:-"); - // idiot check of copied data - Data newAddr(state.address.a.val,6); - BLEMacAddress newMac(newAddr); - HDBG((std::string)newMac); - - - - - // // print out device info - // BLEMacAddress mac(addrData); - // std::string di("Opening Connection :: Device info: mac="); - // di += (std::string)mac; - // di += ", os="; - // auto devPtr = mImpl->db->device(toTarget); - // auto os = devPtr->operatingSystem(); - // if (os.has_value()) { - // if (herald::ble::BLEDeviceOperatingSystem::ios == os) { - // di += "ios"; - // } else if (herald::ble::BLEDeviceOperatingSystem::android == os) { - // di += "android"; - // } - // } else { - // di += "unknown"; - // } - // di += ", ignore="; - // auto ignore = devPtr->ignore(); - // if (ignore) { - // di += "true"; - // } else { - // di += "false"; - // } - - // HDBG(di); - - - // temporarily stop scan - WORKAROUND for https://github.com/zephyrproject-rtos/zephyr/issues/20660 - // HDBG("pausing scanning"); - mImpl->stopScanning(); - mImpl->m_context->getAdvertiser().stopAdvertising(); - // HDBG("Scanning paused"); - - - // attempt connection, if required - bool ok = true; - if (NULL == state.connection) { - HDBG(" - No existing connection. Attempting to connect"); - // std::stringstream os; - // os << " - Create Param: Interval: " << zephyrinternal::defaultCreateParam.interval - // << ", Window: " << zephyrinternal::defaultCreateParam.window - // << ", Timeout: " << zephyrinternal::defaultCreateParam.timeout - // << " | Conn Param: Interval Min: " << zephyrinternal::BTLEConnParam->interval_min - // << ", Interval Max: " << zephyrinternal::defaultConnParam.interval_max - // << ", latency: " << zephyrinternal::defaultConnParam.latency - // << ", timeout: " << zephyrinternal::defaultConnParam.timeout - // << std::ends - // ; - // HDBG(os.str()); - // HDBG(std::to_string(zephyrinternal::defaultCreateParam.interval)); - // HDBG(std::to_string(zephyrinternal::defaultCreateParam.window)); - // HDBG(std::to_string(zephyrinternal::defaultCreateParam.timeout)); - // HDBG(std::to_string(zephyrinternal::defaultConnParam.interval_min)); - // HDBG(std::to_string(zephyrinternal::defaultConnParam.interval_max)); - // HDBG(std::to_string(zephyrinternal::defaultConnParam.latency)); - // HDBG(std::to_string(zephyrinternal::defaultConnParam.timeout)); - // HDBG("Random address check ok?"); - // HDBG(bt_le_scan_random_addr_check() ? "yes" : "no"); - - char addr_str[BT_ADDR_LE_STR_LEN]; - bt_addr_le_to_str(&state.address, addr_str, sizeof(addr_str)); - HDBG("ADDR AS STRING in openConnection:-"); - HDBG(addr_str); - - state.state = BLEDeviceState::connecting; // this is used by the condition variable - state.remoteInstigated = false; // as we're now definitely the instigators - int success = bt_conn_le_create( - &state.address, - &zephyrinternal::defaultCreateParam, - &zephyrinternal::defaultConnParam, - &state.connection - ); - HDBG(" - post connection attempt"); - if (0 != success) { - ok = false; - if (-EINVAL == success) { - HDBG(" - ERROR in passed in parameters"); - } else if (-EAGAIN == success) { - HDBG(" - bt device not ready"); - } else if (-EALREADY == success) { - HDBG(" - bt device initiating") - } else if (-ENOMEM == success) { - HDBG(" - bt connect attempt failed with default BT ID. Trying again later."); - // auto device = mImpl->db->device(toTarget); - // device->ignore(true); - } else if (-ENOBUFS == success) { - HDBG(" - bt_hci_cmd_create has no buffers free"); - } else if (-ECONNREFUSED == success) { - HDBG(" - Connection refused"); - } else if (-EIO == success) { - HDBG(" - Low level BT HCI opcode IO failure"); - } else { - HDBG(" - Unknown error code..."); - HDBG(std::to_string(success)); - } - - // Add to ignore list for now - // DONT DO THIS HERE - MANY REASONS IT CAN FAIL auto device = mImpl->db->device(toTarget); - // HDBG(" - Ignoring following target: {}", toTarget); - // device->ignore(true); - - // Log last disconnected time in BLE database (records failure, allows progressive backoff) - auto device = mImpl->db->device(newMac); // Find by actual current physical address - device->state(BLEDeviceState::disconnected); - - // Immediately restart advertising on failure, but not scanning - mImpl->m_context->getAdvertiser().startAdvertising(); - - return false; - } else { - HDBG("Zephyr waitWithTimeout for new connection"); - // lock and wait for connection to be created - - // STD::ASYNC/MUTEX variant:- - // std::unique_lock lk(mImpl->bleInUse); - // mImpl->connectionAvailable.wait(lk, [this] { - // return mImpl->connectionState == BLEDeviceState::connecting; - // }); // BLOCKS - // verify connection successful - // connCallback(toTarget,mImpl->connectionState == BLEDeviceState::connected); - - // ZEPHYR SPECIFIC VARIANT - uint32_t timedOut = waitWithTimeout(5'000, K_MSEC(25), [&state] { - return state.state == BLEDeviceState::connecting; - }); - if (timedOut != 0) { - HDBG("ZEPHYR WAIT TIMED OUT. Is connected?"); - HDBG((state.state == BLEDeviceState::connected) ? "true" : "false"); - HDBG(std::to_string(timedOut)); - return false; - } - // return mImpl->connectionState == BLEDeviceState::connected; - return state.state == BLEDeviceState::connected; - } - } else { - HDBG(" - Existing connection exists! Reusing."); - return true; - } -} - -// void -// ConcreteBLEReceiver::closeConnection(const TargetIdentifier& toTarget, const HeraldConnectionCallback& connCallback) -// { -// connCallback(toTarget,false); -// } - -// void -// ConcreteBLEReceiver::serviceDiscovery(Activity activity, CompletionCallback callback) -// { -// callback(activity,{}); -// } - -// void -// ConcreteBLEReceiver::readPayload(Activity activity, CompletionCallback callback) -// { -// callback(activity,{}); -// } - -// void -// ConcreteBLEReceiver::immediateSend(Activity activity, CompletionCallback callback) -// { -// callback(activity,{}); -// } - -// void -// ConcreteBLEReceiver::immediateSendAll(Activity activity, CompletionCallback callback) -// { -// callback(activity,{}); -// } - -template -bool -ConcreteBLEReceiver::closeConnection(const TargetIdentifier& toTarget) -{ - HDBG("closeConnection call for ADDR:-"); - ConnectedDeviceState& state = mImpl->findOrCreateState(toTarget); - char addr_str[BT_ADDR_LE_STR_LEN]; - bt_addr_le_to_str(&state.address, addr_str, sizeof(addr_str)); - HDBG(addr_str); - if (NULL != state.connection) { - if (state.remoteInstigated) { - HDBG("Connection remote instigated - not forcing close"); - } else { - bt_conn_disconnect(state.connection, BT_HCI_ERR_REMOTE_USER_TERM_CONN); - // auto device = mImpl->db.device(toTarget); - // device->registerDisconnect(Date()); - } - } else { - // Can clear the remote instigated flag as they've closed the connection - state.remoteInstigated = false; - } - if (!state.remoteInstigated) { - mImpl->removeState(toTarget); - return false; // assumes we've closed it // TODO proper multi-connection state tracking - } - return true; // remote instigated the connection - keep it open and inform caller -} - -template -void -ConcreteBLEReceiver::restartScanningAndAdvertising() -{ - // Print out current list of devices and their info - if (!mImpl->connectionStates.empty()) { - HDBG("Current connection states cached:-"); - for (auto& [key,value] : mImpl->connectionStates) { - std::string ci = " - "; - ci += ((Data)value.target).hexEncodedString(); - ci += " state: "; - switch (value.state) { - case BLEDeviceState::connected: - ci += "connected"; - break; - case BLEDeviceState::disconnected: - ci += "disconnected"; - break; - default: - ci += "connecting"; - } - ci += " connection is null: "; - ci += (NULL == value.connection ? "true" : "false"); - HDBG(ci); - - // Check connection reference is valid by address - has happened with non connectable devices (VR headset bluetooth stations) - value.connection = bt_conn_lookup_addr_le(BT_ID_DEFAULT, &value.address); - // If the above returns null, the next iterator will remove our state - - // Check for non null connection but disconnected state - - if (BLEDeviceState::disconnected == value.state) { - value.connection = NULL; - } - // Now check for timeout - nRF Connect doesn't cause a disconnect callback - if (NULL != value.connection && value.remoteInstigated) { - HDBG("REMOTELY INSTIGATED OR CONNECTED DEVICE TIMED OUT"); - auto device = mImpl->db->device(value.target); - if (device->timeIntervalSinceConnected() < TimeInterval::never() && - device->timeIntervalSinceConnected() > TimeInterval::seconds(30)) { - // disconnect - bt_conn_disconnect(value.connection, BT_HCI_ERR_REMOTE_USER_TERM_CONN); - value.connection = NULL; - } - } - } - - // Do internal clean up too - remove states no longer required - for (auto iter = mImpl->connectionStates.begin();mImpl->connectionStates.end() != iter; ++iter) { - if (NULL == iter->second.connection) { // means Zephyr callbacks are finished with the connection object (i.e. disconnect was called) - mImpl->connectionStates.erase(iter); - } - } - } - - // Restart scanning - // HDBG("restartScanningAndAdvertising - requesting scanning and advertising restarts"); - mImpl->startScanning(); - mImpl->m_context->getAdvertiser().startAdvertising(); -} - -template -std::optional -ConcreteBLEReceiver::serviceDiscovery(Activity activity) -{ - auto currentTargetOpt = std::get<1>(activity.prerequisites.front()); - if (!currentTargetOpt.has_value()) { - HDBG("No target specified for serviceDiscovery activity. Returning."); - return {}; // We've been asked to connect to no specific target - not valid for Bluetooth - } - // Ensure we have a cached state (i.e. we are connected) - auto& state = mImpl->findOrCreateState(currentTargetOpt.value()); - if (state.state != BLEDeviceState::connected) { - HDBG("Not connected to target of activity. Returning."); - return {}; - } - if (NULL == state.connection) { - HDBG("State for activity does not have a connection. Returning."); - return {}; - } - auto device = mImpl->db->device(currentTargetOpt.value()); - - mImpl->gatt_discover(state.connection); - - uint32_t timedOut = waitWithTimeout(5'000, K_MSEC(25), [&device] () -> bool { - return !device->hasServicesSet(); // service discovery not completed yet - }); - - if (0 != timedOut) { - HDBG("service discovery timed out for device"); - HDBG(std::to_string(timedOut)); - return {}; - } - return {}; -} - -template -std::optional -ConcreteBLEReceiver::readPayload(Activity activity) -{ - return {}; -} - -template -std::optional -ConcreteBLEReceiver::immediateSend(Activity activity) -{ - return {}; -} - -template -std::optional -ConcreteBLEReceiver::immediateSendAll(Activity activity) -{ - return {}; -} } } diff --git a/herald/src/ble/zephyr/concrete_ble_transmitter.cpp b/herald/src/ble/zephyr/concrete_ble_transmitter.cpp index 7f04f92..b409faf 100644 --- a/herald/src/ble/zephyr/concrete_ble_transmitter.cpp +++ b/herald/src/ble/zephyr/concrete_ble_transmitter.cpp @@ -2,16 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 // -#include "herald/zephyr_context.h" -#include "herald/ble/ble_concrete.h" #include "herald/ble/zephyr/concrete_ble_transmitter.h" -#include "herald/ble/ble_database.h" -#include "herald/ble/ble_receiver.h" -#include "herald/ble/ble_sensor.h" -#include "herald/ble/ble_sensor_configuration.h" -#include "herald/ble/ble_transmitter.h" -#include "herald/ble/bluetooth_state_manager.h" -#include "herald/data/sensor_logger.h" // nRF Connect SDK includes #include @@ -24,8 +15,6 @@ #include // C++17 includes -#include -#include #include namespace herald { @@ -35,9 +24,8 @@ using namespace herald; using namespace herald::datatype; using namespace herald::data; -static PayloadDataSupplier* latestPds = NULL; - namespace zephyrinternal { + [[maybe_unused]] static void get_tx_power(uint8_t handle_type, uint16_t handle, int8_t *tx_pwr_lvl) { struct bt_hci_cp_vs_read_tx_power_level *cp; @@ -72,315 +60,97 @@ namespace zephyrinternal { net_buf_unref(rsp); } -} - -template -class ConcreteBLETransmitter::Impl { -public: - Impl(ContextT& ctx, BluetoothStateManager& bluetoothStateManager, - std::shared_ptr payloadDataSupplier, std::shared_ptr bleDatabase); - ~Impl(); - - void startAdvertising(); - void stopAdvertising(); - - ContextT& m_context; - BluetoothStateManager& m_stateManager; - std::shared_ptr m_pds; - std::shared_ptr m_db; - - std::vector> delegates; - - bool isAdvertising; - HLOGGER(ContextT); -}; + // template + // class ConcreteBLETransmitter::Impl { + // public: + // Impl(ContextT& ctx, BluetoothStateManager& bluetoothStateManager, + // std::shared_ptr payloadDataSupplier, std::shared_ptr bleDatabase); + // ~Impl(); -/* Herald Service Variables */ -static struct bt_uuid_128 herald_uuid = BT_UUID_INIT_128( - 0x9b, 0xfd, 0x5b, 0xd6, 0x72, 0x45, 0x1e, 0x80, 0xd3, 0x42, 0x46, 0x47, 0xaf, 0x32, 0x81, 0x42 -); -static struct bt_uuid_128 herald_char_signal_uuid = BT_UUID_INIT_128( - 0x11, 0x1a, 0x82, 0x80, 0x9a, 0xe0, 0x24, 0x83, 0x7a, 0x43, 0x2e, 0x09, 0x13, 0xb8, 0x17, 0xf6 -); -static struct bt_uuid_128 herald_char_payload_uuid = BT_UUID_INIT_128( - 0xe7, 0x33, 0x89, 0x8f, 0xe3, 0x43, 0x21, 0xa1, 0x29, 0x48, 0x05, 0x8f, 0xf8, 0xc0, 0x98, 0x3e -); + // void startAdvertising(); + // void stopAdvertising(); -// TODO bind the below to a class and control their values + // ContextT& m_context; + // BluetoothStateManager& m_stateManager; + // std::shared_ptr m_pds; + // std::shared_ptr m_db; -static uint8_t vnd_value[] = { 'V', 'e', 'n', 'd', 'o', 'r' }; + // std::vector> delegates; -static ssize_t read_vnd(struct bt_conn *conn, const struct bt_gatt_attr *attr, - void *buf, uint16_t len, uint16_t offset) -{ - const char *value = (const char*)attr->user_data; + // bool isAdvertising; - return bt_gatt_attr_read(conn, attr, buf, len, offset, value, - strlen(value)); -} + // HLOGGER(ContextT); + // }; -static ssize_t write_vnd(struct bt_conn *conn, const struct bt_gatt_attr *attr, - const void *buf, uint16_t len, uint16_t offset, - uint8_t flags) -{ - uint8_t *value = (uint8_t*)attr->user_data; - if (offset + len > sizeof(vnd_value)) { - return BT_GATT_ERR(BT_ATT_ERR_INVALID_OFFSET); - } + // TODO bind the below to a class and control their values - memcpy(value + offset, buf, len); + static uint8_t vnd_value[] = { 'V', 'e', 'n', 'd', 'o', 'r' }; - return len; -} + static ssize_t read_vnd(struct bt_conn *conn, const struct bt_gatt_attr *attr, + void *buf, uint16_t len, uint16_t offset) + { + const char *value = (const char*)attr->user_data; -static ssize_t read_payload(struct bt_conn *conn, const struct bt_gatt_attr *attr, - void *buf, uint16_t len, uint16_t offset) -{ - const char *value = (const char*)attr->user_data; - if (NULL != latestPds) { - PayloadTimestamp pts; // now - auto payload = latestPds->payload(pts,nullptr); - if (payload.has_value()) { - char* newvalue = new char[payload->size()]; - std::size_t i; - for (i = 0;i < payload->size();i++) { - newvalue[i] = (char)payload->at(i); - } - value = newvalue; - return bt_gatt_attr_read(conn, attr, buf, len, offset, value, - payload->size()); - // } else { - // value = "venue value"; // TODO replace with the use of PDS - } + return bt_gatt_attr_read(conn, attr, buf, len, offset, value, + strlen(value)); } - return bt_gatt_attr_read(conn, attr, buf, len, offset, value, - strlen(value)); -} -static ssize_t write_payload(struct bt_conn *conn, const struct bt_gatt_attr *attr, - const void *buf, uint16_t len, uint16_t offset, - uint8_t flags) -{ - uint8_t *value = (uint8_t*)attr->user_data; - - if (offset + len > sizeof(vnd_value)) { - return BT_GATT_ERR(BT_ATT_ERR_INVALID_OFFSET); - } - - memcpy(value + offset, buf, len); - - return len; -} - -// Define kernel memory statically so we definitely have it -BT_GATT_SERVICE_DEFINE(herald_svc, - BT_GATT_PRIMARY_SERVICE(&herald_uuid), - BT_GATT_CHARACTERISTIC(&herald_char_signal_uuid.uuid, - BT_GATT_CHRC_WRITE, - BT_GATT_PERM_WRITE, - read_vnd,write_vnd, nullptr), - BT_GATT_CHARACTERISTIC(&herald_char_payload_uuid.uuid, - BT_GATT_CHRC_READ, - BT_GATT_PERM_READ, - read_payload, write_payload, nullptr) -); -static auto bp = BT_LE_ADV_CONN_NAME; // No TxPower support -// static auto bp = BT_LE_ADV_PARAM(BT_LE_ADV_OPT_CONNECTABLE | \ -// BT_LE_ADV_OPT_USE_NAME, \ -// BT_GAP_ADV_FAST_INT_MIN_2, \ -// BT_GAP_ADV_FAST_INT_MAX_2, \ -// BT_LE_ADV_OPT_USE_TX_POWER, \ -// NULL); // txpower - REQUIRES EXT ADV OPT on Zephyr (experimental) -static struct bt_data ad[] = { - BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)), - // BT_DATA_BYTES(BT_DATA_TX_POWER, 0x00 ), // See https://github.com/vmware/herald-for-cpp/issues/26 - BT_DATA_BYTES(BT_DATA_UUID16_ALL, - BT_UUID_16_ENCODE(BT_UUID_DIS_VAL), - BT_UUID_16_ENCODE(BT_UUID_GATT_VAL), - BT_UUID_16_ENCODE(BT_UUID_GAP_VAL) - ), - BT_DATA_BYTES(BT_DATA_UUID128_ALL, - 0x9b, 0xfd, 0x5b, 0xd6, 0x72, 0x45, 0x1e, 0x80, - 0xd3, 0x42, 0x46, 0x47, 0xaf, 0x32, 0x81, 0x42 - ), -}; -// WRONG: Android and iOS need the service UUID Herald is discovered via GATT, not via Advert :- -// See https://github.com/vmware/herald-for-cpp/issues/26 - // BT_DATA_BYTES(BT_DATA_UUID128_ALL, - // 0x9b, 0xfd, 0x5b, 0xd6, 0x72, 0x45, 0x1e, 0x80, - // 0xd3, 0x42, 0x46, 0x47, 0xaf, 0x32, 0x81, 0x42 - // ), -// BT_DATA_BYTES(BT_DATA_TX_POWER, 0x00 ), // 'too big advertising data' if both included - -// #ifdef CONFIG_BT_CTLR_TX_PWR - // BT_DATA_BYTES(BT_DATA_TX_POWER, CONFIG_BT_CTLR_TX_PWR ), -// #endif - - -template -ConcreteBLETransmitter::Impl::Impl( - ContextT& ctx, BluetoothStateManager& bluetoothStateManager, - std::shared_ptr payloadDataSupplier, std::shared_ptr bleDatabase) - : m_context(ctx), - m_stateManager(bluetoothStateManager), - m_pds(payloadDataSupplier), - m_db(bleDatabase), - delegates(), - isAdvertising(false) - - HLOGGERINIT(ctx,"Sensor","BLE.ConcreteBLETransmitter") -{ - latestPds = m_pds.get(); -} - -template -ConcreteBLETransmitter::Impl::~Impl() -{ - latestPds = NULL; -} + static ssize_t write_vnd(struct bt_conn *conn, const struct bt_gatt_attr *attr, + const void *buf, uint16_t len, uint16_t offset, + uint8_t flags) + { + uint8_t *value = (uint8_t*)attr->user_data; -template -void -ConcreteBLETransmitter::Impl::startAdvertising() -{ - // HTDBG("startAdvertising called"); - if (!BLESensorConfiguration::advertisingEnabled) { - HTDBG("Sensor Configuration has advertising disabled. Returning."); - return; - } - if (isAdvertising) { - // HTDBG("Already advertising. Returning."); - return; - } + if (offset + len > sizeof(vnd_value)) { + return BT_GATT_ERR(BT_ATT_ERR_INVALID_OFFSET); + } - // Note: TxPower currently disabled due to restricted advert space and the need to include Herald's service. - // See https://github.com/vmware/herald-for-cpp/issues/26 - // Get current TxPower and alter advert accordingly:- - // int8_t txp_get = 0; - // zephyrinternal::get_tx_power(BT_HCI_VS_LL_HANDLE_TYPE_ADV,0, &txp_get); - // HTDBG("Zephyr tx power:-"); - // HTDBG(std::to_string(txp_get)); - // herald::ble::ad[1] = bt_data{ - // .type=BT_DATA_TX_POWER, - // .data_len=sizeof(txp_get), - // .data=(const uint8_t *)uint8_t(txp_get) - // }; + memcpy(value + offset, buf, len); - // Now start advertising - // See https://developer.nordicsemi.com/nRF_Connect_SDK/doc/latest/zephyr/reference/bluetooth/gap.html#group__bt__gap_1gac45d16bfe21c3c38e834c293e5ebc42b - int success = bt_le_adv_start(herald::ble::bp, herald::ble::ad, ARRAY_SIZE(herald::ble::ad), NULL, 0); - if (0 != success) { - HTDBG("Start advertising failed"); - return; + return len; } - - // zephyrinternal::get_tx_power(BT_HCI_VS_LL_HANDLE_TYPE_ADV,0, &txp_get); - // HTDBG("Zephyr tx power post advertising starting:-"); - // HTDBG(std::to_string(txp_get)); - HTDBG("Start advertising completed successfully"); - isAdvertising = true; -} - -template -void -ConcreteBLETransmitter::Impl::stopAdvertising() -{ - // HTDBG("stopAdvertising called"); - if (!BLESensorConfiguration::advertisingEnabled) { - HTDBG("Sensor Configuration has advertising disabled. Returning."); - return; - } - if (!isAdvertising) { - // HTDBG("Not advertising already. Returning."); - return; - } - isAdvertising = false; - int success = bt_le_adv_stop(); - if (0 != success) { - HTDBG("Stop advertising failed"); - return; + static ssize_t read_payload(struct bt_conn *conn, const struct bt_gatt_attr *attr, + void *buf, uint16_t len, uint16_t offset) + { + const char *value = (const char*)attr->user_data; + if (NULL != latestPds) { + PayloadTimestamp pts; // now + auto payload = latestPds->payload(pts,nullptr); + if (payload.has_value()) { + char* newvalue = new char[payload->size()]; + std::size_t i; + for (i = 0;i < payload->size();i++) { + newvalue[i] = (char)payload->at(i); + } + value = newvalue; + return bt_gatt_attr_read(conn, attr, buf, len, offset, value, + payload->size()); + // } else { + // value = "venue value"; // TODO replace with the use of PDS + } + } + return bt_gatt_attr_read(conn, attr, buf, len, offset, value, + strlen(value)); } - // Don't stop Bluetooth altogether - this is done by the ZephyrContext->stopBluetooth() function only - HTDBG("Stop advertising completed successfully"); -} - - - - - - - -template -ConcreteBLETransmitter::ConcreteBLETransmitter( - ContextT& ctx, BluetoothStateManager& bluetoothStateManager, - std::shared_ptr payloadDataSupplier, std::shared_ptr bleDatabase) - : mImpl(std::make_shared(ctx,bluetoothStateManager, - payloadDataSupplier,bleDatabase)) -{ - ; -} - -template -ConcreteBLETransmitter::~ConcreteBLETransmitter() -{ - // stop(); // stops using m_addr -} - -template -std::optional> -ConcreteBLETransmitter::coordinationProvider() -{ - return {}; -} + static ssize_t write_payload(struct bt_conn *conn, const struct bt_gatt_attr *attr, + const void *buf, uint16_t len, uint16_t offset, + uint8_t flags) + { + uint8_t *value = (uint8_t*)attr->user_data; -template -void -ConcreteBLETransmitter::add(const std::shared_ptr& delegate) -{ - mImpl->delegates.push_back(delegate); -} + if (offset + len > sizeof(vnd_value)) { + return BT_GATT_ERR(BT_ATT_ERR_INVALID_OFFSET); + } -// 6. Implement any additional transmitter functionality, as required + memcpy(value + offset, buf, len); -template -void -ConcreteBLETransmitter::start() -{ - HDBG("ConcreteBLETransmitter::start"); - if (!BLESensorConfiguration::advertisingEnabled) { - HDBG("Sensor Configuration has advertising disabled. Returning."); - return; + return len; } - mImpl->m_context->getAdvertiser().registerStopCallback([this] () -> void { - mImpl->stopAdvertising(); - }); - mImpl->m_context->getAdvertiser().registerStartCallback([this] () -> void { - mImpl->startAdvertising(); - }); - HDBG("Advertising callbacks registered"); - - // Ensure our zephyr context has bluetooth ready - mImpl->m_context->startBluetooth(); - - HDBG("Bluetooth started. Requesting start of adverts"); - mImpl->startAdvertising(); -} - -template -void -ConcreteBLETransmitter::stop() -{ - HDBG("ConcreteBLETransmitter::stop"); - if (!BLESensorConfiguration::advertisingEnabled) { - HDBG("Sensor Configuration has advertising disabled. Returning."); - return; - } - mImpl->stopAdvertising(); } } diff --git a/herald/src/data/devnull_logging_sink.cpp b/herald/src/data/devnull_logging_sink.cpp new file mode 100644 index 0000000..7934a6e --- /dev/null +++ b/herald/src/data/devnull_logging_sink.cpp @@ -0,0 +1,21 @@ +// Copyright 2020-2021 Herald Project Contributors +// SPDX-License-Identifier: Apache-2.0 +// + +#include "herald/data/sensor_logger.h" +#include "herald/data/devnull_logging_sink.h" + +#include + +namespace herald::data { + +DevNullLoggingSink::DevNullLoggingSink() = default; +DevNullLoggingSink::~DevNullLoggingSink() = default; + +void +DevNullLoggingSink::log(const std::string& subsystem, const std::string& category, SensorLoggerLevel level, std::string message) +{ + ; // Literally do nothing... like cat-ing to /dev/null +} + +} \ No newline at end of file diff --git a/herald/src/zephyr_context.cpp b/herald/src/zephyr_context.cpp index 891f20e..f309005 100644 --- a/herald/src/zephyr_context.cpp +++ b/herald/src/zephyr_context.cpp @@ -110,9 +110,9 @@ ZephyrContextProvider::getBluetoothStateManager() } void -ZephyrContextProvider::add(std::shared_ptr delegate) +ZephyrContextProvider::add(BluetoothStateManagerDelegate& delegate) { - stateDelegates.push_back(delegate); + stateDelegates.emplace_back(delegate); } BluetoothState @@ -175,8 +175,8 @@ ZephyrContextProvider::enableBluetooth() noexcept if (0 == success) { bluetoothEnabled = true; - for (auto delegate : stateDelegates) { - delegate->bluetoothStateManager(BluetoothState::poweredOn); + for (auto& delegate : stateDelegates) { + delegate.get().bluetoothStateManager(BluetoothState::poweredOn); } } else { LOG_INF("Error enabling Zephyr Bluetooth: %d", success); @@ -197,8 +197,8 @@ ZephyrContextProvider::startBluetooth() noexcept int ZephyrContextProvider::stopBluetooth() noexcept { - for (auto delegate : stateDelegates) { - delegate->bluetoothStateManager(BluetoothState::poweredOff); + for (auto& delegate : stateDelegates) { + delegate.get().bluetoothStateManager(BluetoothState::poweredOff); } return 0; } From 5d2c5237796e3d15dbeb6f2ad665d6267b90d47a Mon Sep 17 00:00:00 2001 From: Adam Fowler Date: Sat, 10 Apr 2021 18:51:57 +0100 Subject: [PATCH 22/28] Compiles and deploys to nRF52833 Known issues:- - Not advertising Herald service - Not seeing scan results in logging Signed-off-by: Adam Fowler --- herald-wearable/src/main.cpp | 8 +- herald/include/herald/ble/ble_concrete.h | 6 +- herald/include/herald/ble/ble_coordinator.h | 16 +- .../herald/ble/ble_sensor_configuration.h | 44 ++- .../herald/ble/zephyr/concrete_ble_receiver.h | 354 ++++++------------ .../ble/zephyr/concrete_ble_transmitter.h | 79 ++-- herald/include/herald/context.h | 15 +- .../herald/data/zephyr/zephyr_logging_sink.h | 5 +- .../datatype/signal_characteristic_data.h | 24 +- herald/src/ble/ble_sensor_configuration.cpp | 42 ++- .../src/ble/zephyr/concrete_ble_receiver.cpp | 251 +++++++++++++ .../ble/zephyr/concrete_ble_transmitter.cpp | 116 +++++- .../src/data/zephyr/zephyr_logging_sink.cpp | 6 + .../datatype/signal_characteristic_data.cpp | 62 +-- herald/src/zephyr_context.cpp | 10 +- 15 files changed, 644 insertions(+), 394 deletions(-) diff --git a/herald-wearable/src/main.cpp b/herald-wearable/src/main.cpp index 649c436..1c0e9ed 100644 --- a/herald-wearable/src/main.cpp +++ b/herald-wearable/src/main.cpp @@ -42,12 +42,12 @@ #include #include -namespace applogging { +// namespace applogging { LOG_MODULE_REGISTER(app, CONFIG_APP_LOG_LEVEL); #define APP_DBG(_msg,...) LOG_DBG(_msg,##__VA_ARGS__); #define APP_INF(_msg,...) LOG_INF(_msg,##__VA_ARGS__); #define APP_ERR(_msg,...) LOG_ERR(_msg,##__VA_ARGS__); -} +// } /* 1000 msec = 1 sec */ #define SLEEP_TIME_MS 1000 @@ -283,7 +283,9 @@ void herald_entry() { sink2.log("subsys2","cat2",SensorLoggerLevel::fault,"Herald error message"); // Enable transmitter (i.e. this is a Herald device) - BLESensorConfiguration::advertisingEnabled = true; + BLESensorConfiguration config = ctx.getSensorConfiguration(); // copy ctor + config.advertisingEnabled = true; + ctx.setSensorConfiguration(config); APP_DBG("Creating sensor array"); diff --git a/herald/include/herald/ble/ble_concrete.h b/herald/include/herald/ble/ble_concrete.h index e3bd130..d2bb050 100644 --- a/herald/include/herald/ble/ble_concrete.h +++ b/herald/include/herald/ble/ble_concrete.h @@ -71,7 +71,8 @@ class ConcreteBLESensor : public BLESensor, public BLEDatabaseDelegate, public: ConcreteBLESensor(ContextT& ctx, BluetoothStateManager& bluetoothStateManager, std::shared_ptr payloadDataSupplier) - : database(ctx), + : m_context(ctx), + database(ctx), stateManager(bluetoothStateManager), transmitter(ctx, bluetoothStateManager, payloadDataSupplier, database), receiver(ctx, bluetoothStateManager, payloadDataSupplier, database), @@ -89,7 +90,7 @@ class ConcreteBLESensor : public BLESensor, public BLEDatabaseDelegate, // Coordination overrides - Since v1.2-beta3 std::optional> coordinationProvider() override { // Only return this if we support scanning - if (BLESensorConfiguration::scanningEnabled) { + if (m_context.getSensorConfiguration().scanningEnabled) { HTDBG("Providing a BLECoordinationProvider"); //return std::optional>(std::static_cast(coordinator)); return coordinator; @@ -226,6 +227,7 @@ class ConcreteBLESensor : public BLESensor, public BLEDatabaseDelegate, // Data members hidden by PIMPL + ContextT& m_context; ConcreteBLEDatabase database; BluetoothStateManager& stateManager; ConcreteBLETransmitter> transmitter; diff --git a/herald/include/herald/ble/ble_coordinator.h b/herald/include/herald/ble/ble_coordinator.h index 2ad8410..1352892 100644 --- a/herald/include/herald/ble/ble_coordinator.h +++ b/herald/include/herald/ble/ble_coordinator.h @@ -184,10 +184,10 @@ class HeraldProtocolBLECoordinationProvider : public CoordinationProvider { // Add all targets in database that are not known - auto newConns = db.matches([](const std::shared_ptr& device) -> bool { + auto newConns = db.matches([this](const std::shared_ptr& device) -> bool { return !device->ignore() && ( - !device->hasService(BLESensorConfiguration::serviceUUID) + !device->hasService(context.getSensorConfiguration().serviceUUID) || !device->payloadData().has_value() // Know the OS, but not the payload (ID) || @@ -286,21 +286,21 @@ class HeraldProtocolBLECoordinationProvider : public CoordinationProvider { // auto state0Devices = db.matches([](std::shared_ptr device) -> bool { // return !device->ignore() && !device->pseudoDeviceAddress().has_value(); // }); - auto state1Devices = db.matches([](const std::shared_ptr& device) -> bool { + auto state1Devices = db.matches([this](const std::shared_ptr& device) -> bool { return !device->ignore() && !device->receiveOnly() && - !device->hasService(BLESensorConfiguration::serviceUUID); + !device->hasService(context.getSensorConfiguration().serviceUUID); }); - auto state2Devices = db.matches([](const std::shared_ptr& device) -> bool { + auto state2Devices = db.matches([this](const std::shared_ptr& device) -> bool { return !device->ignore() && !device->receiveOnly() && - device->hasService(BLESensorConfiguration::serviceUUID) && + device->hasService(context.getSensorConfiguration().serviceUUID) && !device->payloadData().has_value(); // TODO check for Herald transferred payload data (not legacy) }); - auto state4Devices = db.matches([](const std::shared_ptr& device) -> bool { + auto state4Devices = db.matches([this](const std::shared_ptr& device) -> bool { return !device->ignore() && !device->receiveOnly() && - device->hasService(BLESensorConfiguration::serviceUUID) && + device->hasService(context.getSensorConfiguration().serviceUUID) && device->immediateSendData().has_value(); }); // TODO State X (timed out / out of range) devices filter check -> Then remove from BLEDatabase diff --git a/herald/include/herald/ble/ble_sensor_configuration.h b/herald/include/herald/ble/ble_sensor_configuration.h index f3de893..099d844 100644 --- a/herald/include/herald/ble/ble_sensor_configuration.h +++ b/herald/include/herald/ble/ble_sensor_configuration.h @@ -10,10 +10,14 @@ namespace herald { namespace ble { + +using namespace herald::datatype; /// Defines BLE sensor configuration data, e.g. service and characteristic UUIDs -namespace BLESensorConfiguration { - using namespace herald::datatype; +struct BLESensorConfiguration { + BLESensorConfiguration(); + BLESensorConfiguration(const BLESensorConfiguration& other); + ~BLESensorConfiguration() = default; // MARK:- BLE service and characteristic UUID, and manufacturer ID @@ -22,63 +26,63 @@ namespace BLESensorConfiguration { /// then discover services to identify actual beacons. /// - Service and characteristic UUIDs are V4 UUIDs that have been randomly generated and tested /// for uniqueness by conducting web searches to ensure it returns no results. - const UUID serviceUUID = UUID::fromString("428132af-4746-42d3-801e-4572d65bfd9b"); + UUID serviceUUID; /// Signaling characteristic for controlling connection between peripheral and central, e.g. keep each other from suspend state /// - Characteristic UUID is randomly generated V4 UUIDs that has been tested for uniqueness by conducting web searches to ensure it returns no results. - const UUID androidSignalCharacteristicUUID = UUID::fromString("f617b813-092e-437a-8324-e09a80821a11"); + UUID androidSignalCharacteristicUUID; /// Signaling characteristic for controlling connection between peripheral and central, e.g. keep each other from suspend state /// - Characteristic UUID is randomly generated V4 UUIDs that has been tested for uniqueness by conducting web searches to ensure it returns no results. - const UUID iosSignalCharacteristicUUID = UUID::fromString("0eb0d5f2-eae4-4a9a-8af3-a4adb02d4363"); + UUID iosSignalCharacteristicUUID; /// Primary payload characteristic (read) for distributing payload data from peripheral to central, e.g. identity data /// - Characteristic UUID is randomly generated V4 UUIDs that has been tested for uniqueness by conducting web searches to ensure it returns no results. - const UUID payloadCharacteristicUUID = UUID::fromString("3e98c0f8-8f05-4829-a121-43e38f8933e7"); + UUID payloadCharacteristicUUID; /// Manufacturer data is being used on Android to store pseudo device address /// - Pending update to dedicated ID - constexpr int manufacturerIdForSensor = 65530; + int manufacturerIdForSensor; /// BLE advert manufacturer ID for Apple, for scanning of background iOS devices - constexpr int manufacturerIdForApple = 76; + int manufacturerIdForApple; // MARK:- BLE signal characteristic action codes /// Signal characteristic action code for write payload, expect 1 byte action code followed by 2 byte little-endian Int16 integer value for payload data length, then payload data - constexpr std::byte signalCharacteristicActionWritePayload = (std::byte) 1; + std::byte signalCharacteristicActionWritePayload; /// Signal characteristic action code for write RSSI, expect 1 byte action code followed by 4 byte little-endian Int32 integer value for RSSI value - constexpr std::byte signalCharacteristicActionWriteRSSI = (std::byte) 2; + std::byte signalCharacteristicActionWriteRSSI; /// Signal characteristic action code for write payload, expect 1 byte action code followed by 2 byte little-endian Int16 integer value for payload sharing data length, then payload sharing data - constexpr std::byte signalCharacteristicActionWritePayloadSharing = (std::byte) 3; + std::byte signalCharacteristicActionWritePayloadSharing; /// Arbitrary immediate write - constexpr std::byte signalCharacteristicActionWriteImmediate = (std::byte) 4; + std::byte signalCharacteristicActionWriteImmediate; // MARK:- App configurable BLE features /// Log level for BLESensor - //constexpr SensorLoggerLevel logLevel = SensorLoggerLevel.debug; + //SensorLoggerLevel logLevel = SensorLoggerLevel.debug; /// Payload update at regular intervals, in addition to default HERALD communication process. /// - Use this to enable regular payload reads according to app payload lifespan. /// - Set to .never to disable this function. /// - Payload updates are reported to SensorDelegate as didRead. /// - Setting take immediate effect, no need to restart BLESensor, can also be applied while BLESensor is active. - const TimeInterval payloadDataUpdateTimeInterval = TimeInterval::never(); + TimeInterval payloadDataUpdateTimeInterval; /// Expiry time for shared payloads, to ensure only recently seen payloads are shared - const TimeInterval payloadSharingExpiryTimeInterval = TimeInterval::minutes(5); + TimeInterval payloadSharingExpiryTimeInterval; /// Advert refresh time interval - const TimeInterval advertRefreshTimeInterval = TimeInterval::minutes(15); + TimeInterval advertRefreshTimeInterval; /// Connection management /// Max connections - since v1.2 (allowing multiple connections on Android and C++) - const int maxBluetoothConnections = 20; // Same as NRF 52840 max connections + int maxBluetoothConnections; // Same as NRF 52840 max connections // Does this Herald application support advertising? - static bool advertisingEnabled = true; + bool advertisingEnabled; // Does this Herald application support scanning? (Simple Venue Beacons don't) - static bool scanningEnabled = true; + bool scanningEnabled; -} // end namespace +}; // end struct } // end namespace } // end namespace diff --git a/herald/include/herald/ble/zephyr/concrete_ble_receiver.h b/herald/include/herald/ble/zephyr/concrete_ble_receiver.h index 425b5f7..dc34512 100644 --- a/herald/include/herald/ble/zephyr/concrete_ble_receiver.h +++ b/herald/include/herald/ble/zephyr/concrete_ble_receiver.h @@ -52,38 +52,7 @@ using namespace herald::payload; // ZEPHYR UTILITY FUNCTIONS /** wait with timeout for Zephyr. Returns true if the function timed out rather than completed **/ -uint32_t waitWithTimeout(uint32_t timeoutMillis, k_timeout_t period, std::function keepWaiting) -{ - uint32_t start_time; - uint32_t stop_time; - uint32_t millis_spent; - bool notComplete = keepWaiting(); - if (!notComplete) { - return 0; - } - /* capture initial time stamp */ - start_time = k_uptime_get_32(); - - /* capture final time stamp */ - stop_time = k_uptime_get_32(); - /* compute how long the work took (assumes no counter rollover) */ - millis_spent = stop_time - start_time; - - while (millis_spent < timeoutMillis && notComplete) { - k_sleep(period); - notComplete = keepWaiting(); - - /* capture final time stamp */ - stop_time = k_uptime_get_32(); - /* compute how long the work took (assumes no counter rollover) */ - millis_spent = stop_time - start_time; - } - if (notComplete) { - return millis_spent; - } else { - return 0; - } -} +uint32_t waitWithTimeout(uint32_t timeoutMillis, k_timeout_t period, std::function keepWaiting); struct ConnectedDeviceState { ConnectedDeviceState(const TargetIdentifier& id) @@ -105,80 +74,29 @@ struct ConnectedDeviceState { namespace zephyrinternal { - /* Herald Service Variables */ - static struct bt_uuid_128 herald_uuid = BT_UUID_INIT_128( - 0x9b, 0xfd, 0x5b, 0xd6, 0x72, 0x45, 0x1e, 0x80, 0xd3, 0x42, 0x46, 0x47, 0xaf, 0x32, 0x81, 0x42 - ); - static struct bt_uuid_128 herald_char_signal_android_uuid = BT_UUID_INIT_128( - 0x11, 0x1a, 0x82, 0x80, 0x9a, 0xe0, 0x24, 0x83, 0x7a, 0x43, 0x2e, 0x09, 0x13, 0xb8, 0x17, 0xf6 - ); - static struct bt_uuid_128 herald_char_signal_ios_uuid = BT_UUID_INIT_128( - 0x63, 0x43, 0x2d, 0xb0, 0xad, 0xa4, 0xf3, 0x8a, 0x9a, 0x4a, 0xe4, 0xea, 0xf2, 0xd5, 0xb0, 0x0e - ); - static struct bt_uuid_128 herald_char_payload_uuid = BT_UUID_INIT_128( - 0xe7, 0x33, 0x89, 0x8f, 0xe3, 0x43, 0x21, 0xa1, 0x29, 0x48, 0x05, 0x8f, 0xf8, 0xc0, 0x98, 0x3e - ); - + struct bt_uuid_128* getHeraldUUID(); + struct bt_uuid_128* getHeraldSignalAndroidCharUUID(); + struct bt_uuid_128* getHeraldSignalIOSCharUUID(); + struct bt_uuid_128* getHeraldPayloadCharUUID(); - bt_le_conn_param* BTLEConnParam = BT_LE_CONN_PARAM_DEFAULT; // BT_LE_CONN_PARAM(0x018,3200,0,400); // NOT BT_LE_CONN_PARAM_DEFAULT; - bt_conn_le_create_param* BTLECreateParam = BT_CONN_LE_CREATE_CONN; // BT_CONN_LE_CREATE_PARAM(BT_CONN_LE_OPT_NONE, 0x0010,0x0010);// NOT BT_CONN_LE_CREATE_CONN; - - static struct bt_conn_le_create_param defaultCreateParam = BT_CONN_LE_CREATE_PARAM_INIT( - BT_CONN_LE_OPT_NONE, BT_GAP_SCAN_FAST_INTERVAL, BT_GAP_SCAN_FAST_INTERVAL - ); - static struct bt_le_conn_param defaultConnParam = BT_LE_CONN_PARAM_INIT( - //BT_GAP_INIT_CONN_INT_MIN, BT_GAP_INIT_CONN_INT_MAX, 0, 400 - //12, 12 // aka 15ms, default from apple documentation - 0x50, 0x50, // aka 80ms, from nRF SDK LLPM sample - 0, 400 - ); - // Note for apple see: https://developer.apple.com/library/archive/qa/qa1931/_index.html - // And https://developer.apple.com/accessories/Accessory-Design-Guidelines.pdf (BLE section) + void setReceiverInstance(herald::zephyrinternal::Callbacks& cbref); + void resetReceiverInstance(); - [[maybe_unused]] - static struct bt_le_scan_param defaultScanParam = //BT_LE_SCAN_PASSIVE; - { - .type = BT_LE_SCAN_TYPE_PASSIVE, // passive scan - .options = BT_LE_SCAN_OPT_FILTER_DUPLICATE, // Scans for EVERYTHING - .interval = BT_GAP_SCAN_FAST_INTERVAL, // 0x0010, // V.FAST, NOT BT_GAP_SCAN_FAST_INTERVAL - gap.h - .window = BT_GAP_SCAN_FAST_WINDOW // 0x0010, // V.FAST, NOT BT_GAP_SCAN_FAST_INTERVAL - gap.h - }; - - /** - * Why is this necessary? Traditional pointer-to-function cannot easily - * and reliably be wrapped with std::function/bind/mem_fn. We also need - * the Herald API to use subclasses for each platform, necessitating - * some sort of static bridge. Not pretty, but works and allows us to - * prevent nullptr problems - */ - std::optional> - concreteReceiverInstance; + struct bt_conn_le_create_param* getDefaultCreateParam(); + struct bt_le_conn_param* getDefaultConnParam(); - + struct bt_le_scan_param* getDefaultScanParam(); + struct bt_scan_init_param* getScanInitParam(); + + struct bt_gatt_read_params* getReadParams(); // static struct bt_conn* conn = NULL; [[maybe_unused]] // NOTE: The below is called multiple times for ONE char value. Keep appending to result until NULL==data. - static uint8_t gatt_read_cb(struct bt_conn *conn, uint8_t err, + uint8_t gatt_read_cb(struct bt_conn *conn, uint8_t err, struct bt_gatt_read_params *params, - const void *data, uint16_t length) - { - if (concreteReceiverInstance.has_value()) { - return concreteReceiverInstance.value().get().gatt_read_cb(conn,err,params,data,length); - } - return length; // say we've consumed the data anyway - } - - [[maybe_unused]] - static struct bt_gatt_read_params read_params = { - .func = gatt_read_cb, - .handle_count = 1, - .single = { - .handle = 0x0000, - .offset = 0x0000 - } - }; + const void *data, uint16_t length); // void scan_init(void) @@ -211,55 +129,25 @@ namespace zephyrinternal { [[maybe_unused]] - static void connected(struct bt_conn *conn, uint8_t err) - { - if (concreteReceiverInstance.has_value()) { - concreteReceiverInstance.value().get().connected(conn,err); - } - } + void connected(struct bt_conn *conn, uint8_t err); [[maybe_unused]] - static void disconnected(struct bt_conn *conn, uint8_t reason) - { - if (concreteReceiverInstance.has_value()) { - concreteReceiverInstance.value().get().disconnected(conn,reason); - } - } + void disconnected(struct bt_conn *conn, uint8_t reason); [[maybe_unused]] - static void le_param_updated(struct bt_conn *conn, uint16_t interval, - uint16_t latency, uint16_t timeout) - { - if (concreteReceiverInstance.has_value()) { - concreteReceiverInstance.value().get().le_param_updated(conn,interval,latency,timeout); - } - } - - [[maybe_unused]] - static struct bt_conn_cb conn_callbacks = { - .connected = connected, - .disconnected = disconnected, - .le_param_updated = le_param_updated, - }; + void le_param_updated(struct bt_conn *conn, uint16_t interval, + uint16_t latency, uint16_t timeout); + + struct bt_conn_cb* getConnectionCallbacks(); // static bt_addr_le_t *last_addr = BT_ADDR_LE_NONE; [[maybe_unused]] void scan_cb(const bt_addr_le_t *addr, int8_t rssi, uint8_t adv_type, - struct net_buf_simple *buf) { - if (concreteReceiverInstance.has_value()) { - concreteReceiverInstance.value().get().scan_cb(addr,rssi,adv_type,buf); - } - } + struct net_buf_simple *buf); + // BT_SCAN_CB_INIT(scan_cbs, scan_filter_match, ); - - [[maybe_unused]] - static struct bt_scan_init_param scan_init = { - .scan_param = &defaultScanParam, - .connect_if_match = false, - .conn_param = &defaultConnParam - }; // void scan_filter_match(struct bt_scan_device_info *device_info, // struct bt_scan_filter_match *filter_match, @@ -315,37 +203,17 @@ namespace zephyrinternal { // GATT DISCOVERY INTERNAL METHODS - static void discovery_completed_cb(struct bt_gatt_dm *dm, - void *context) - { - if (concreteReceiverInstance.has_value()) { - concreteReceiverInstance.value().get().discovery_completed_cb(dm,context); - } - } + void discovery_completed_cb(struct bt_gatt_dm *dm, + void *context); - static void discovery_service_not_found_cb(struct bt_conn *conn, - void *context) - { - if (concreteReceiverInstance.has_value()) { - concreteReceiverInstance.value().get().discovery_service_not_found_cb(conn,context); - } - } + void discovery_service_not_found_cb(struct bt_conn *conn, + void *context); - static void discovery_error_found_cb(struct bt_conn *conn, - int err, - void *context) - { - if (concreteReceiverInstance.has_value()) { - concreteReceiverInstance.value().get().discovery_error_found_cb(conn,err,context); - } - } - - static const struct bt_gatt_dm_cb discovery_cb = { - .completed = discovery_completed_cb, - .service_not_found = discovery_service_not_found_cb, - .error_found = discovery_error_found_cb, - }; + void discovery_error_found_cb(struct bt_conn *conn, + int err, + void *context); + const struct bt_gatt_dm_cb* getDiscoveryCallbacks(); } template @@ -395,48 +263,48 @@ class ConcreteBLEReceiver : public BLEReceiver, public HeraldProtocolV1Provider, void start() override { - HDBG("ConcreteBLEReceiver::start"); - if (!BLESensorConfiguration::scanningEnabled) { - HDBG("Sensor Configuration has scanning disabled. Returning."); + HTDBG("ConcreteBLEReceiver::start"); + if (!m_context.getSensorConfiguration().scanningEnabled) { + HTDBG("Sensor Configuration has scanning disabled. Returning."); return; } - herald::ble::zephyrinternal::concreteReceiverInstance.emplace(*this); + herald::ble::zephyrinternal::setReceiverInstance(*this); // Ensure our zephyr context has bluetooth ready - HDBG("calling start bluetooth"); + HTDBG("calling start bluetooth"); int startOk = m_context.getPlatform().startBluetooth(); - HDBG("start bluetooth done"); + HTDBG("start bluetooth done"); if (0 != startOk) { - HDBG("ERROR starting context bluetooth:-"); - HDBG(std::to_string(startOk)); + HTDBG("ERROR starting context bluetooth:-"); + HTDBG(std::to_string(startOk)); } - HDBG("Calling conn cb register"); - bt_conn_cb_register(&zephyrinternal::conn_callbacks); - HDBG("conn cb register done"); + HTDBG("Calling conn cb register"); + bt_conn_cb_register(zephyrinternal::getConnectionCallbacks()); + HTDBG("conn cb register done"); - HDBG("calling bt scan start"); + HTDBG("calling bt scan start"); startScanning(); - HDBG("ConcreteBLEReceiver::start completed successfully"); + HTDBG("ConcreteBLEReceiver::start completed successfully"); } void stop() override { - HDBG("ConcreteBLEReceiver::stop"); - if (!BLESensorConfiguration::scanningEnabled) { - HDBG("Sensor Configuration has scanning disabled. Returning."); + HTDBG("ConcreteBLEReceiver::stop"); + if (!m_context.getSensorConfiguration().scanningEnabled) { + HTDBG("Sensor Configuration has scanning disabled. Returning."); return; } - herald::ble::zephyrinternal::concreteReceiverInstance.reset(); // destroys the shared_ptr not necessarily the underlying value + herald::ble::zephyrinternal::resetReceiverInstance(); // destroys the shared_ptr not necessarily the underlying value stopScanning(); // Don't stop Bluetooth altogether - this is done by the ZephyrContext->stopBluetooth() function only - HDBG("ConcreteBLEReceiver::stop completed successfully"); + HTDBG("ConcreteBLEReceiver::stop completed successfully"); } // Herald V1 protocol provider overrides @@ -451,7 +319,7 @@ class ConcreteBLEReceiver : public BLEReceiver, public HeraldProtocolV1Provider, // NON C++17 VERSION:- bool openConnection(const TargetIdentifier& toTarget) override { - HDBG("openConnection"); + HTDBG("openConnection"); // Create addr from TargetIdentifier data ConnectedDeviceState& state = findOrCreateState(toTarget); @@ -482,11 +350,11 @@ class ConcreteBLEReceiver : public BLEReceiver, public HeraldProtocolV1Provider, bt_addr_le_t tempAddress{1, {{val[0],val[1],val[2],val[3],val[4],val[5]}}}; // state.address = BT_ADDR_LE_NONE; bt_addr_le_copy(&state.address, &tempAddress); - HDBG("Address copied. Constituted as:-"); + HTDBG("Address copied. Constituted as:-"); // idiot check of copied data Data newAddr(state.address.a.val,6); BLEMacAddress newMac(newAddr); - HDBG((std::string)newMac); + HTDBG((std::string)newMac); @@ -515,20 +383,20 @@ class ConcreteBLEReceiver : public BLEReceiver, public HeraldProtocolV1Provider, // di += "false"; // } - // HDBG(di); + // HTDBG(di); // temporarily stop scan - WORKAROUND for https://github.com/zephyrproject-rtos/zephyr/issues/20660 - // HDBG("pausing scanning"); + // HTDBG("pausing scanning"); stopScanning(); m_context.getPlatform().getAdvertiser().stopAdvertising(); - // HDBG("Scanning paused"); + // HTDBG("Scanning paused"); // attempt connection, if required bool ok = true; if (NULL == state.connection) { - HDBG(" - No existing connection. Attempting to connect"); + HTDBG(" - No existing connection. Attempting to connect"); // std::stringstream os; // os << " - Create Param: Interval: " << zephyrinternal::defaultCreateParam.interval // << ", Window: " << zephyrinternal::defaultCreateParam.window @@ -539,57 +407,57 @@ class ConcreteBLEReceiver : public BLEReceiver, public HeraldProtocolV1Provider, // << ", timeout: " << zephyrinternal::defaultConnParam.timeout // << std::ends // ; - // HDBG(os.str()); - // HDBG(std::to_string(zephyrinternal::defaultCreateParam.interval)); - // HDBG(std::to_string(zephyrinternal::defaultCreateParam.window)); - // HDBG(std::to_string(zephyrinternal::defaultCreateParam.timeout)); - // HDBG(std::to_string(zephyrinternal::defaultConnParam.interval_min)); - // HDBG(std::to_string(zephyrinternal::defaultConnParam.interval_max)); - // HDBG(std::to_string(zephyrinternal::defaultConnParam.latency)); - // HDBG(std::to_string(zephyrinternal::defaultConnParam.timeout)); - // HDBG("Random address check ok?"); - // HDBG(bt_le_scan_random_addr_check() ? "yes" : "no"); + // HTDBG(os.str()); + // HTDBG(std::to_string(zephyrinternal::defaultCreateParam.interval)); + // HTDBG(std::to_string(zephyrinternal::defaultCreateParam.window)); + // HTDBG(std::to_string(zephyrinternal::defaultCreateParam.timeout)); + // HTDBG(std::to_string(zephyrinternal::defaultConnParam.interval_min)); + // HTDBG(std::to_string(zephyrinternal::defaultConnParam.interval_max)); + // HTDBG(std::to_string(zephyrinternal::defaultConnParam.latency)); + // HTDBG(std::to_string(zephyrinternal::defaultConnParam.timeout)); + // HTDBG("Random address check ok?"); + // HTDBG(bt_le_scan_random_addr_check() ? "yes" : "no"); char addr_str[BT_ADDR_LE_STR_LEN]; bt_addr_le_to_str(&state.address, addr_str, sizeof(addr_str)); - HDBG("ADDR AS STRING in openConnection:-"); - HDBG(addr_str); + HTDBG("ADDR AS STRING in openConnection:-"); + HTDBG(addr_str); state.state = BLEDeviceState::connecting; // this is used by the condition variable state.remoteInstigated = false; // as we're now definitely the instigators int success = bt_conn_le_create( &state.address, - &zephyrinternal::defaultCreateParam, - &zephyrinternal::defaultConnParam, + zephyrinternal::getDefaultCreateParam(), + zephyrinternal::getDefaultConnParam(), &state.connection ); - HDBG(" - post connection attempt"); + HTDBG(" - post connection attempt"); if (0 != success) { ok = false; if (-EINVAL == success) { - HDBG(" - ERROR in passed in parameters"); + HTDBG(" - ERROR in passed in parameters"); } else if (-EAGAIN == success) { - HDBG(" - bt device not ready"); + HTDBG(" - bt device not ready"); } else if (-EALREADY == success) { - HDBG(" - bt device initiating") + HTDBG(" - bt device initiating") } else if (-ENOMEM == success) { - HDBG(" - bt connect attempt failed with default BT ID. Trying again later."); + HTDBG(" - bt connect attempt failed with default BT ID. Trying again later."); // auto device = db.device(toTarget); // device->ignore(true); } else if (-ENOBUFS == success) { - HDBG(" - bt_hci_cmd_create has no buffers free"); + HTDBG(" - bt_hci_cmd_create has no buffers free"); } else if (-ECONNREFUSED == success) { - HDBG(" - Connection refused"); + HTDBG(" - Connection refused"); } else if (-EIO == success) { - HDBG(" - Low level BT HCI opcode IO failure"); + HTDBG(" - Low level BT HCI opcode IO failure"); } else { - HDBG(" - Unknown error code..."); - HDBG(std::to_string(success)); + HTDBG(" - Unknown error code..."); + HTDBG(std::to_string(success)); } // Add to ignore list for now // DONT DO THIS HERE - MANY REASONS IT CAN FAIL auto device = db.device(toTarget); - // HDBG(" - Ignoring following target: {}", toTarget); + // HTDBG(" - Ignoring following target: {}", toTarget); // device->ignore(true); // Log last disconnected time in BLE database (records failure, allows progressive backoff) @@ -601,7 +469,7 @@ class ConcreteBLEReceiver : public BLEReceiver, public HeraldProtocolV1Provider, return false; } else { - HDBG("Zephyr waitWithTimeout for new connection"); + HTDBG("Zephyr waitWithTimeout for new connection"); // lock and wait for connection to be created // STD::ASYNC/MUTEX variant:- @@ -617,16 +485,16 @@ class ConcreteBLEReceiver : public BLEReceiver, public HeraldProtocolV1Provider, return state.state == BLEDeviceState::connecting; }); if (timedOut != 0) { - HDBG("ZEPHYR WAIT TIMED OUT. Is connected?"); - HDBG((state.state == BLEDeviceState::connected) ? "true" : "false"); - HDBG(std::to_string(timedOut)); + HTDBG("ZEPHYR WAIT TIMED OUT. Is connected?"); + HTDBG((state.state == BLEDeviceState::connected) ? "true" : "false"); + HTDBG(std::to_string(timedOut)); return false; } // return connectionState == BLEDeviceState::connected; return state.state == BLEDeviceState::connected; } } else { - HDBG(" - Existing connection exists! Reusing."); + HTDBG(" - Existing connection exists! Reusing."); return true; } } @@ -635,14 +503,14 @@ class ConcreteBLEReceiver : public BLEReceiver, public HeraldProtocolV1Provider, bool closeConnection(const TargetIdentifier& toTarget) override { - HDBG("closeConnection call for ADDR:-"); + HTDBG("closeConnection call for ADDR:-"); ConnectedDeviceState& state = findOrCreateState(toTarget); char addr_str[BT_ADDR_LE_STR_LEN]; bt_addr_le_to_str(&state.address, addr_str, sizeof(addr_str)); - HDBG(addr_str); + HTDBG(addr_str); if (NULL != state.connection) { if (state.remoteInstigated) { - HDBG("Connection remote instigated - not forcing close"); + HTDBG("Connection remote instigated - not forcing close"); } else { bt_conn_disconnect(state.connection, BT_HCI_ERR_REMOTE_USER_TERM_CONN); // auto device = db.device(toTarget); @@ -665,7 +533,7 @@ class ConcreteBLEReceiver : public BLEReceiver, public HeraldProtocolV1Provider, { // Print out current list of devices and their info if (!connectionStates.empty()) { - HDBG("Current connection states cached:-"); + HTDBG("Current connection states cached:-"); for (auto& [key,value] : connectionStates) { std::string ci = " - "; ci += ((Data)value.target).hexEncodedString(); @@ -682,7 +550,7 @@ class ConcreteBLEReceiver : public BLEReceiver, public HeraldProtocolV1Provider, } ci += " connection is null: "; ci += (NULL == value.connection ? "true" : "false"); - HDBG(ci); + HTDBG(ci); // Check connection reference is valid by address - has happened with non connectable devices (VR headset bluetooth stations) value.connection = bt_conn_lookup_addr_le(BT_ID_DEFAULT, &value.address); @@ -695,7 +563,7 @@ class ConcreteBLEReceiver : public BLEReceiver, public HeraldProtocolV1Provider, } // Now check for timeout - nRF Connect doesn't cause a disconnect callback if (NULL != value.connection && value.remoteInstigated) { - HDBG("REMOTELY INSTIGATED OR CONNECTED DEVICE TIMED OUT"); + HTDBG("REMOTELY INSTIGATED OR CONNECTED DEVICE TIMED OUT"); auto device = db.device(value.target); if (device->timeIntervalSinceConnected() < TimeInterval::never() && device->timeIntervalSinceConnected() > TimeInterval::seconds(30)) { @@ -715,7 +583,7 @@ class ConcreteBLEReceiver : public BLEReceiver, public HeraldProtocolV1Provider, } // Restart scanning - // HDBG("restartScanningAndAdvertising - requesting scanning and advertising restarts"); + // HTDBG("restartScanningAndAdvertising - requesting scanning and advertising restarts"); startScanning(); m_context.getPlatform().getAdvertiser().startAdvertising(); } @@ -724,17 +592,17 @@ class ConcreteBLEReceiver : public BLEReceiver, public HeraldProtocolV1Provider, { auto currentTargetOpt = std::get<1>(activity.prerequisites.front()); if (!currentTargetOpt.has_value()) { - HDBG("No target specified for serviceDiscovery activity. Returning."); + HTDBG("No target specified for serviceDiscovery activity. Returning."); return {}; // We've been asked to connect to no specific target - not valid for Bluetooth } // Ensure we have a cached state (i.e. we are connected) auto& state = findOrCreateState(currentTargetOpt.value()); if (state.state != BLEDeviceState::connected) { - HDBG("Not connected to target of activity. Returning."); + HTDBG("Not connected to target of activity. Returning."); return {}; } if (NULL == state.connection) { - HDBG("State for activity does not have a connection. Returning."); + HTDBG("State for activity does not have a connection. Returning."); return {}; } auto device = db.device(currentTargetOpt.value()); @@ -746,8 +614,8 @@ class ConcreteBLEReceiver : public BLEReceiver, public HeraldProtocolV1Provider, }); if (0 != timedOut) { - HDBG("service discovery timed out for device"); - HDBG(std::to_string(timedOut)); + HTDBG("service discovery timed out for device"); + HTDBG(std::to_string(timedOut)); return {}; } return {}; @@ -896,10 +764,10 @@ class ConcreteBLEReceiver : public BLEReceiver, public HeraldProtocolV1Provider, // Check for match of uuid to a herald read payload char struct bt_gatt_chrc *chrc = bt_gatt_dm_attr_chrc_val(prev); - int matches = bt_uuid_cmp(chrc->uuid, &zephyrinternal::herald_char_payload_uuid.uuid); + int matches = bt_uuid_cmp(chrc->uuid, &zephyrinternal::getHeraldPayloadCharUUID()->uuid); if (0 == matches) { HTDBG(" - FOUND Herald read characteristic. Reading."); - device->payloadCharacteristic(BLESensorConfiguration::payloadCharacteristicUUID); + device->payloadCharacteristic(m_context.getSensorConfiguration().payloadCharacteristicUUID); // initialise payload data for this state state.readPayload.clear(); @@ -909,9 +777,9 @@ class ConcreteBLEReceiver : public BLEReceiver, public HeraldProtocolV1Provider, // TODO REFACTOR THE ACTUAL FETCHING OF PAYLOAD TO READPAYLOAD FUNCTION // - Actually important, as currently a wearable will request the char multiple times from iOS before a reply is received - zephyrinternal::read_params.single.handle = chrc->value_handle; - zephyrinternal::read_params.single.offset = 0x0000; // gets changed on each use - int readErr = bt_gatt_read(bt_gatt_dm_conn_get(dm), &zephyrinternal::read_params); + zephyrinternal::getReadParams()->single.handle = chrc->value_handle; + zephyrinternal::getReadParams()->single.offset = 0x0000; // gets changed on each use + int readErr = bt_gatt_read(bt_gatt_dm_conn_get(dm), zephyrinternal::getReadParams()); if (readErr) { HTDBG("GATT read error: TBD");//, readErr); // bt_conn_disconnect(bt_gatt_dm_conn_get(dm), BT_HCI_ERR_REMOTE_USER_TERM_CONN); @@ -919,18 +787,18 @@ class ConcreteBLEReceiver : public BLEReceiver, public HeraldProtocolV1Provider, continue; // check for other characteristics too } - matches = bt_uuid_cmp(chrc->uuid, &zephyrinternal::herald_char_signal_android_uuid.uuid); + matches = bt_uuid_cmp(chrc->uuid, &zephyrinternal::getHeraldSignalAndroidCharUUID()->uuid); if (0 == matches) { HTDBG(" - FOUND Herald android signal characteristic. logging."); - device->signalCharacteristic(BLESensorConfiguration::androidSignalCharacteristicUUID); + device->signalCharacteristic(m_context.getSensorConfiguration().androidSignalCharacteristicUUID); device->operatingSystem(BLEDeviceOperatingSystem::android); continue; // check for other characteristics too } - matches = bt_uuid_cmp(chrc->uuid, &zephyrinternal::herald_char_signal_ios_uuid.uuid); + matches = bt_uuid_cmp(chrc->uuid, &zephyrinternal::getHeraldSignalIOSCharUUID()->uuid); if (0 == matches) { HTDBG(" - FOUND Herald ios signal characteristic. logging."); - device->signalCharacteristic(BLESensorConfiguration::iosSignalCharacteristicUUID); + device->signalCharacteristic(m_context.getSensorConfiguration().iosSignalCharacteristicUUID); device->operatingSystem(BLEDeviceOperatingSystem::ios); continue; // check for other characteristics too @@ -958,7 +826,7 @@ class ConcreteBLEReceiver : public BLEReceiver, public HeraldProtocolV1Provider, // very last action - for concurrency reasons (C++17 threading/mutex/async/future not available on Zephyr) std::vector serviceList; - serviceList.push_back(BLESensorConfiguration::serviceUUID); + serviceList.push_back(m_context.getSensorConfiguration().serviceUUID); device->services(serviceList); } @@ -1044,7 +912,7 @@ class ConcreteBLEReceiver : public BLEReceiver, public HeraldProtocolV1Provider, if (isScanning) { return; } - int err = bt_le_scan_start(&zephyrinternal::defaultScanParam, &zephyrinternal::scan_cb); // scan_cb linked via BT_SCAN_CB_INIT call + int err = bt_le_scan_start(zephyrinternal::getDefaultScanParam(), &zephyrinternal::scan_cb); // scan_cb linked via BT_SCAN_CB_INIT call if (0 != err) { HTDBG("Starting scanning failed"); @@ -1067,7 +935,7 @@ class ConcreteBLEReceiver : public BLEReceiver, public HeraldProtocolV1Provider, int err; // begin introspection - err = bt_gatt_dm_start(conn, &zephyrinternal::herald_uuid.uuid, &zephyrinternal::discovery_cb, NULL); + err = bt_gatt_dm_start(conn, &zephyrinternal::getHeraldUUID()->uuid, zephyrinternal::getDiscoveryCallbacks(), NULL); if (err) { HTDBG("could not start the discovery procedure, error code") HTDBG(std::to_string(err)); @@ -1076,7 +944,7 @@ class ConcreteBLEReceiver : public BLEReceiver, public HeraldProtocolV1Provider, } HTDBG("Service discovery succeeded... now do something with it in the callback!"); } - + ContextT& m_context; BluetoothStateManager& m_stateManager; std::shared_ptr m_pds; diff --git a/herald/include/herald/ble/zephyr/concrete_ble_transmitter.h b/herald/include/herald/ble/zephyr/concrete_ble_transmitter.h index e6e6deb..8c0513e 100644 --- a/herald/include/herald/ble/zephyr/concrete_ble_transmitter.h +++ b/herald/include/herald/ble/zephyr/concrete_ble_transmitter.h @@ -44,72 +44,35 @@ using namespace herald::datatype; using namespace herald::ble::filter; using namespace herald::payload; -static PayloadDataSupplier* latestPds = NULL; // zephyr internal functions called by template -/* Herald Service Variables */ -static struct bt_uuid_128 herald_uuid = BT_UUID_INIT_128( - 0x9b, 0xfd, 0x5b, 0xd6, 0x72, 0x45, 0x1e, 0x80, 0xd3, 0x42, 0x46, 0x47, 0xaf, 0x32, 0x81, 0x42 -); -static struct bt_uuid_128 herald_char_signal_uuid = BT_UUID_INIT_128( - 0x11, 0x1a, 0x82, 0x80, 0x9a, 0xe0, 0x24, 0x83, 0x7a, 0x43, 0x2e, 0x09, 0x13, 0xb8, 0x17, 0xf6 -); -static struct bt_uuid_128 herald_char_payload_uuid = BT_UUID_INIT_128( - 0xe7, 0x33, 0x89, 0x8f, 0xe3, 0x43, 0x21, 0xa1, 0x29, 0x48, 0x05, 0x8f, 0xf8, 0xc0, 0x98, 0x3e -); namespace zephyrinternal { - static void get_tx_power(uint8_t handle_type, uint16_t handle, int8_t *tx_pwr_lvl); + PayloadDataSupplier* getPayloadDataSupplier(); + + void setPayloadDataSupplier(PayloadDataSupplier* pds); - static ssize_t read_vnd(struct bt_conn *conn, const struct bt_gatt_attr *attr, + struct bt_data* getAdvertData(); + std::size_t getAdvertDataSize(); + + struct bt_le_adv_param* getAdvertParams(); + + void get_tx_power(uint8_t handle_type, uint16_t handle, int8_t *tx_pwr_lvl); + + ssize_t read_vnd(struct bt_conn *conn, const struct bt_gatt_attr *attr, void *buf, uint16_t len, uint16_t offset); - static ssize_t write_vnd(struct bt_conn *conn, const struct bt_gatt_attr *attr, + ssize_t write_vnd(struct bt_conn *conn, const struct bt_gatt_attr *attr, const void *buf, uint16_t len, uint16_t offset, uint8_t flags); - static ssize_t read_payload(struct bt_conn *conn, const struct bt_gatt_attr *attr, + ssize_t read_payload(struct bt_conn *conn, const struct bt_gatt_attr *attr, void *buf, uint16_t len, uint16_t offset); - static ssize_t write_payload(struct bt_conn *conn, const struct bt_gatt_attr *attr, + ssize_t write_payload(struct bt_conn *conn, const struct bt_gatt_attr *attr, const void *buf, uint16_t len, uint16_t offset, uint8_t flags); } -// Define kernel memory statically so we definitely have it -BT_GATT_SERVICE_DEFINE(herald_svc, - BT_GATT_PRIMARY_SERVICE(&herald_uuid), - BT_GATT_CHARACTERISTIC(&herald_char_signal_uuid.uuid, - BT_GATT_CHRC_WRITE, - BT_GATT_PERM_WRITE, - zephyrinternal::read_vnd,zephyrinternal::write_vnd, nullptr), - BT_GATT_CHARACTERISTIC(&herald_char_payload_uuid.uuid, - BT_GATT_CHRC_READ, - BT_GATT_PERM_READ, - zephyrinternal::read_payload, zephyrinternal::write_payload, nullptr) -); -static auto bp = BT_LE_ADV_CONN_NAME; // No TxPower support -/* -static auto bp = BT_LE_ADV_PARAM(BT_LE_ADV_OPT_CONNECTABLE | \ - BT_LE_ADV_OPT_USE_NAME, \ - BT_GAP_ADV_FAST_INT_MIN_2, \ - BT_GAP_ADV_FAST_INT_MAX_2, \ - BT_LE_ADV_OPT_USE_TX_POWER, \ - NULL); // txpower - REQUIRES EXT ADV OPT on Zephyr (experimental) -*/ -static struct bt_data ad[] = { - BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)), - // BT_DATA_BYTES(BT_DATA_TX_POWER, 0x00 ), // See https://github.com/vmware/herald-for-cpp/issues/26 - BT_DATA_BYTES(BT_DATA_UUID16_ALL, - BT_UUID_16_ENCODE(BT_UUID_DIS_VAL), - BT_UUID_16_ENCODE(BT_UUID_GATT_VAL), - BT_UUID_16_ENCODE(BT_UUID_GAP_VAL) - ), - BT_DATA_BYTES(BT_DATA_UUID128_ALL, - 0x9b, 0xfd, 0x5b, 0xd6, 0x72, 0x45, 0x1e, 0x80, - 0xd3, 0x42, 0x46, 0x47, 0xaf, 0x32, 0x81, 0x42 - ), -}; - template @@ -126,7 +89,7 @@ class ConcreteBLETransmitter : public BLETransmitter { HLOGGERINIT(ctx,"Sensor","BLE.ConcreteBLETransmitter") { - latestPds = m_pds.get(); + zephyrinternal::setPayloadDataSupplier(m_pds.get()); } ConcreteBLETransmitter(const ConcreteBLETransmitter& from) = delete; @@ -135,7 +98,7 @@ class ConcreteBLETransmitter : public BLETransmitter { ~ConcreteBLETransmitter() { stop(); - latestPds = NULL; + zephyrinternal::setPayloadDataSupplier(NULL); } // Coordination overrides - Since v1.2-beta3 @@ -150,7 +113,7 @@ class ConcreteBLETransmitter : public BLETransmitter { void start() override { HTDBG("ConcreteBLETransmitter::start"); - if (!BLESensorConfiguration::advertisingEnabled) { + if (!m_context.getSensorConfiguration().advertisingEnabled) { HTDBG("Sensor Configuration has advertising disabled. Returning."); return; } @@ -172,7 +135,7 @@ class ConcreteBLETransmitter : public BLETransmitter { void stop() override { HTDBG("ConcreteBLETransmitter::stop"); - if (!BLESensorConfiguration::advertisingEnabled) { + if (!m_context.getSensorConfiguration().advertisingEnabled) { HTDBG("Sensor Configuration has advertising disabled. Returning."); return; } @@ -195,7 +158,7 @@ class ConcreteBLETransmitter : public BLETransmitter { void startAdvertising() { // HTDBG("startAdvertising called"); - if (!BLESensorConfiguration::advertisingEnabled) { + if (!m_context.getSensorConfiguration().advertisingEnabled) { HTDBG("Sensor Configuration has advertising disabled. Returning."); return; } @@ -219,7 +182,7 @@ class ConcreteBLETransmitter : public BLETransmitter { // Now start advertising // See https://developer.nordicsemi.com/nRF_Connect_SDK/doc/latest/zephyr/reference/bluetooth/gap.html#group__bt__gap_1gac45d16bfe21c3c38e834c293e5ebc42b - int success = bt_le_adv_start(herald::ble::bp, herald::ble::ad, ARRAY_SIZE(herald::ble::ad), NULL, 0); + int success = bt_le_adv_start(zephyrinternal::getAdvertParams(), zephyrinternal::getAdvertData(), zephyrinternal::getAdvertDataSize(), NULL, 0); if (0 != success) { HTDBG("Start advertising failed"); return; @@ -236,7 +199,7 @@ class ConcreteBLETransmitter : public BLETransmitter { void stopAdvertising() { // HTDBG("stopAdvertising called"); - if (!BLESensorConfiguration::advertisingEnabled) { + if (!m_context.getSensorConfiguration().advertisingEnabled) { HTDBG("Sensor Configuration has advertising disabled. Returning."); return; } diff --git a/herald/include/herald/context.h b/herald/include/herald/context.h index 2bbdcf5..11a680f 100644 --- a/herald/include/herald/context.h +++ b/herald/include/herald/context.h @@ -5,6 +5,8 @@ #ifndef HERALD_CONTEXT_H #define HERALD_CONTEXT_H +#include "ble/ble_sensor_configuration.h" // TODO abstract this away in to platform class + namespace herald { /// @@ -55,9 +57,9 @@ struct Context { using logging_sink_type = LoggingSinkT; Context(PlatformT& platform,LoggingSinkT& sink,BluetoothStateManagerT& bsm) noexcept - : platform(platform), loggingSink(sink), bleStateManager(bsm) {} + : platform(platform), loggingSink(sink), bleStateManager(bsm), config() {} Context(const Context& other) noexcept - : platform(other.platform), loggingSink(other.loggingSink), bleStateManager(other.bleStateManager) + : platform(other.platform), loggingSink(other.loggingSink), bleStateManager(other.bleStateManager), config(other.config) {} // Context(Context&& other) // : loggingSink(std::move(other.loggingSink)), bleStateManager(std::move(other.bleStateManager)) @@ -87,10 +89,19 @@ struct Context { return platform; } + const ble::BLESensorConfiguration& getSensorConfiguration() { + return config; + } + + void setSensorConfiguration(ble::BLESensorConfiguration newConfig) { + config = newConfig; + } + private: PlatformT& platform; LoggingSinkT& loggingSink; BluetoothStateManagerT& bleStateManager; + ble::BLESensorConfiguration config; }; // \brief Default empty platform type for platforms that have no custom functionality diff --git a/herald/include/herald/data/zephyr/zephyr_logging_sink.h b/herald/include/herald/data/zephyr/zephyr_logging_sink.h index 501b59b..dcf1934 100644 --- a/herald/include/herald/data/zephyr/zephyr_logging_sink.h +++ b/herald/include/herald/data/zephyr/zephyr_logging_sink.h @@ -10,13 +10,10 @@ // NOTE: Link Herald to the Zephyr logging system // Set HERALD_LOG_LEVEL=4 for debug in CMake using add_definitions(-DHERALD_LOG_LEVEL=4 ) // Defaults to 0 (OFF) - see herald/data/zephyr/zephyr_logging_sink.h -#include +// #include namespace herald { -// THE BELOW IS DONE IN EXACTLY ONE HERALD FILE -LOG_MODULE_REGISTER(heraldlogger, HERALD_LOG_LEVEL); - namespace data { class ZephyrLoggingSink { diff --git a/herald/include/herald/datatype/signal_characteristic_data.h b/herald/include/herald/datatype/signal_characteristic_data.h index c2ef011..2f88dca 100644 --- a/herald/include/herald/datatype/signal_characteristic_data.h +++ b/herald/include/herald/datatype/signal_characteristic_data.h @@ -10,6 +10,7 @@ #include "payload_data.h" #include "payload_sharing_data.h" #include "immediate_send_data.h" +#include "../ble/ble_sensor_configuration.h" #include @@ -21,38 +22,39 @@ enum class SignalCharacteristicDataType : short { }; namespace SignalCharacteristicData { +using namespace herald::ble; [[maybe_unused]] -static std::optional encodeWriteRssi(const RSSI& rssi) noexcept; +std::optional encodeWriteRssi(const BLESensorConfiguration& config,const RSSI& rssi) noexcept; [[maybe_unused]] -static std::optional decodeWriteRSSI(const Data& data) noexcept; +std::optional decodeWriteRSSI(const BLESensorConfiguration& config,const Data& data) noexcept; [[maybe_unused]] -static std::optional encodeWritePayload(const PayloadData& payloadData) noexcept; +std::optional encodeWritePayload(const BLESensorConfiguration& config,const PayloadData& payloadData) noexcept; [[maybe_unused]] -static std::optional decodeWritePayload(const Data& data) noexcept; +std::optional decodeWritePayload(const BLESensorConfiguration& config,const Data& data) noexcept; [[maybe_unused]] -static std::optional encodeWritePayloadSharing(const PayloadSharingData& payloadSharingData) noexcept; +std::optional encodeWritePayloadSharing(const BLESensorConfiguration& config,const PayloadSharingData& payloadSharingData) noexcept; [[maybe_unused]] -static std::optional decodeWritePayloadSharing(const Data& data) noexcept; +std::optional decodeWritePayloadSharing(const BLESensorConfiguration& config,const Data& data) noexcept; [[maybe_unused]] -static std::optional encodeImmediateSend(const ImmediateSendData& immediateSendData) noexcept; +std::optional encodeImmediateSend(const BLESensorConfiguration& config,const ImmediateSendData& immediateSendData) noexcept; [[maybe_unused]] -static std::optional decodeImmediateSend(const Data& data) noexcept; +std::optional decodeImmediateSend(const BLESensorConfiguration& config,const Data& data) noexcept; [[maybe_unused]] -static SignalCharacteristicDataType detect(const Data& data) noexcept; +SignalCharacteristicDataType detect(const BLESensorConfiguration& config,const Data& data) noexcept; // THE FOLLOWING METHODS ARE MOVED TO THE CPP AND THUS HIDDEN FROM THE ABI -//static byte signalDataActionCode(const byte[] signalData); +//byte signalDataActionCode(const byte[] signalData); -//static bool int16(const byte[] data, const std::size_t index, int16& to); /// true if successful, sets to parameters +//bool int16(const byte[] data, const std::size_t index, int16& to); /// true if successful, sets to parameters } // end namespace diff --git a/herald/src/ble/ble_sensor_configuration.cpp b/herald/src/ble/ble_sensor_configuration.cpp index a8f29f9..33b75e2 100644 --- a/herald/src/ble/ble_sensor_configuration.cpp +++ b/herald/src/ble/ble_sensor_configuration.cpp @@ -6,8 +6,48 @@ namespace herald { namespace ble { -namespace BLESensorConfiguration { +BLESensorConfiguration::BLESensorConfiguration() + : serviceUUID(UUID::fromString("428132af-4746-42d3-801e-4572d65bfd9b")), + androidSignalCharacteristicUUID(UUID::fromString("f617b813-092e-437a-8324-e09a80821a11")), + iosSignalCharacteristicUUID(UUID::fromString("0eb0d5f2-eae4-4a9a-8af3-a4adb02d4363")), + payloadCharacteristicUUID(UUID::fromString("3e98c0f8-8f05-4829-a121-43e38f8933e7")), + manufacturerIdForSensor(65530), + manufacturerIdForApple(76), + signalCharacteristicActionWritePayload(std::byte(1)), + signalCharacteristicActionWriteRSSI(std::byte(2)), + signalCharacteristicActionWritePayloadSharing(std::byte(3)), + signalCharacteristicActionWriteImmediate(std::byte(4)), + payloadDataUpdateTimeInterval(TimeInterval::never()), + payloadSharingExpiryTimeInterval(TimeInterval::minutes(5)), + advertRefreshTimeInterval(TimeInterval::minutes(15)), + maxBluetoothConnections(20), + advertisingEnabled(true), + scanningEnabled(true) +{ + ; } + +BLESensorConfiguration::BLESensorConfiguration(const BLESensorConfiguration& other) + : serviceUUID(other.serviceUUID), + androidSignalCharacteristicUUID(other.androidSignalCharacteristicUUID), + iosSignalCharacteristicUUID(other.iosSignalCharacteristicUUID), + payloadCharacteristicUUID(other.payloadCharacteristicUUID), + manufacturerIdForSensor(other.manufacturerIdForSensor), + manufacturerIdForApple(other.manufacturerIdForApple), + signalCharacteristicActionWritePayload(other.signalCharacteristicActionWritePayload), + signalCharacteristicActionWriteRSSI(other.signalCharacteristicActionWriteRSSI), + signalCharacteristicActionWritePayloadSharing(other.signalCharacteristicActionWritePayloadSharing), + signalCharacteristicActionWriteImmediate(other.signalCharacteristicActionWriteImmediate), + payloadDataUpdateTimeInterval(other.payloadDataUpdateTimeInterval), + payloadSharingExpiryTimeInterval(other.payloadSharingExpiryTimeInterval), + advertRefreshTimeInterval(other.advertRefreshTimeInterval), + maxBluetoothConnections(other.maxBluetoothConnections), + advertisingEnabled(other.advertisingEnabled), + scanningEnabled(other.scanningEnabled) +{ + ; +} + } } \ No newline at end of file diff --git a/herald/src/ble/zephyr/concrete_ble_receiver.cpp b/herald/src/ble/zephyr/concrete_ble_receiver.cpp index ff291dc..c74c142 100644 --- a/herald/src/ble/zephyr/concrete_ble_receiver.cpp +++ b/herald/src/ble/zephyr/concrete_ble_receiver.cpp @@ -7,6 +7,257 @@ namespace herald { namespace ble { +uint32_t waitWithTimeout(uint32_t timeoutMillis, k_timeout_t period, std::function keepWaiting) +{ + uint32_t start_time; + uint32_t stop_time; + uint32_t millis_spent; + bool notComplete = keepWaiting(); + if (!notComplete) { + return 0; + } + /* capture initial time stamp */ + start_time = k_uptime_get_32(); + + /* capture final time stamp */ + stop_time = k_uptime_get_32(); + /* compute how long the work took (assumes no counter rollover) */ + millis_spent = stop_time - start_time; + + while (millis_spent < timeoutMillis && notComplete) { + k_sleep(period); + notComplete = keepWaiting(); + + /* capture final time stamp */ + stop_time = k_uptime_get_32(); + /* compute how long the work took (assumes no counter rollover) */ + millis_spent = stop_time - start_time; + } + if (notComplete) { + return millis_spent; + } else { + return 0; + } +} + +namespace zephyrinternal { + // Items that can only be defined in a single translation unit (cpp file):- + + + /* Herald Service Variables */ + struct bt_uuid_128 herald_uuid = BT_UUID_INIT_128( + 0x9b, 0xfd, 0x5b, 0xd6, 0x72, 0x45, 0x1e, 0x80, 0xd3, 0x42, 0x46, 0x47, 0xaf, 0x32, 0x81, 0x42 + ); + struct bt_uuid_128 herald_char_signal_android_uuid = BT_UUID_INIT_128( + 0x11, 0x1a, 0x82, 0x80, 0x9a, 0xe0, 0x24, 0x83, 0x7a, 0x43, 0x2e, 0x09, 0x13, 0xb8, 0x17, 0xf6 + ); + struct bt_uuid_128 herald_char_signal_ios_uuid = BT_UUID_INIT_128( + 0x63, 0x43, 0x2d, 0xb0, 0xad, 0xa4, 0xf3, 0x8a, 0x9a, 0x4a, 0xe4, 0xea, 0xf2, 0xd5, 0xb0, 0x0e + ); + struct bt_uuid_128 herald_char_payload_uuid = BT_UUID_INIT_128( + 0xe7, 0x33, 0x89, 0x8f, 0xe3, 0x43, 0x21, 0xa1, 0x29, 0x48, 0x05, 0x8f, 0xf8, 0xc0, 0x98, 0x3e + ); + struct bt_uuid_128* getHeraldUUID() { + return &herald_uuid; + } + struct bt_uuid_128* getHeraldSignalAndroidCharUUID() { + return &herald_char_signal_android_uuid; + } + struct bt_uuid_128* getHeraldSignalIOSCharUUID() { + return &herald_char_signal_ios_uuid; + } + struct bt_uuid_128* getHeraldPayloadCharUUID() { + return &herald_char_payload_uuid; + } + + + bt_le_conn_param* BTLEConnParam = BT_LE_CONN_PARAM_DEFAULT; // BT_LE_CONN_PARAM(0x018,3200,0,400); // NOT BT_LE_CONN_PARAM_DEFAULT; + bt_conn_le_create_param* BTLECreateParam = BT_CONN_LE_CREATE_CONN; // BT_CONN_LE_CREATE_PARAM(BT_CONN_LE_OPT_NONE, 0x0010,0x0010);// NOT BT_CONN_LE_CREATE_CONN; + + struct bt_conn_le_create_param defaultCreateParam = BT_CONN_LE_CREATE_PARAM_INIT( + BT_CONN_LE_OPT_NONE, BT_GAP_SCAN_FAST_INTERVAL, BT_GAP_SCAN_FAST_INTERVAL + ); + struct bt_le_conn_param defaultConnParam = BT_LE_CONN_PARAM_INIT( + //BT_GAP_INIT_CONN_INT_MIN, BT_GAP_INIT_CONN_INT_MAX, 0, 400 + //12, 12 // aka 15ms, default from apple documentation + 0x50, 0x50, // aka 80ms, from nRF SDK LLPM sample + 0, 400 + ); + + struct bt_conn_le_create_param* getDefaultCreateParam() { + return &defaultCreateParam; + } + + struct bt_le_conn_param* getDefaultConnParam() { + return &defaultConnParam; + } + + // Note for apple see: https://developer.apple.com/library/archive/qa/qa1931/_index.html + // And https://developer.apple.com/accessories/Accessory-Design-Guidelines.pdf (BLE section) + + [[maybe_unused]] + struct bt_le_scan_param defaultScanParam = //BT_LE_SCAN_PASSIVE; + { + .type = BT_LE_SCAN_TYPE_PASSIVE, // passive scan + .options = BT_LE_SCAN_OPT_FILTER_DUPLICATE, // Scans for EVERYTHING + .interval = BT_GAP_SCAN_FAST_INTERVAL, // 0x0010, // V.FAST, NOT BT_GAP_SCAN_FAST_INTERVAL - gap.h + .window = BT_GAP_SCAN_FAST_WINDOW // 0x0010, // V.FAST, NOT BT_GAP_SCAN_FAST_INTERVAL - gap.h + }; + + struct bt_le_scan_param* getDefaultScanParam() { + return &defaultScanParam; + } + + [[maybe_unused]] + struct bt_scan_init_param scan_init = { + .scan_param = &defaultScanParam, + .connect_if_match = false, + .conn_param = &defaultConnParam + }; + + struct bt_scan_init_param* getScanInitParam() { + return &scan_init; + } + + /** + * Why is this necessary? Traditional pointer-to-function cannot easily + * and reliably be wrapped with std::function/bind/mem_fn. We also need + * the Herald API to use subclasses for each platform, necessitating + * some sort of static bridge. Not pretty, but works and allows us to + * prevent nullptr problems + */ + std::optional> + concreteReceiverInstance; + + void setReceiverInstance(herald::zephyrinternal::Callbacks& cbref) { + concreteReceiverInstance.emplace(cbref); + } + + void resetReceiverInstance() { + concreteReceiverInstance.reset(); + } + + [[maybe_unused]] + struct bt_gatt_read_params read_params = { + .func = gatt_read_cb, + .handle_count = 1, + .single = { + .handle = 0x0000, + .offset = 0x0000 + } + }; + + struct bt_gatt_read_params* getReadParams() { + return &read_params; + } + + + + + + // Functions that provide access (defined in the H file, implemented here):- + + [[maybe_unused]] + void connected(struct bt_conn *conn, uint8_t err) + { + if (concreteReceiverInstance.has_value()) { + concreteReceiverInstance.value().get().connected(conn,err); + } + } + + [[maybe_unused]] + void disconnected(struct bt_conn *conn, uint8_t reason) + { + if (concreteReceiverInstance.has_value()) { + concreteReceiverInstance.value().get().disconnected(conn,reason); + } + } + + [[maybe_unused]] + void le_param_updated(struct bt_conn *conn, uint16_t interval, + uint16_t latency, uint16_t timeout) + { + if (concreteReceiverInstance.has_value()) { + concreteReceiverInstance.value().get().le_param_updated(conn,interval,latency,timeout); + } + } + + [[maybe_unused]] + struct bt_conn_cb conn_callbacks = { + .connected = connected, + .disconnected = disconnected, + .le_param_updated = le_param_updated, + }; + + /// \brief Returns singleton connection callback struct reference + struct bt_conn_cb* getConnectionCallbacks() { + return &conn_callbacks; + } + + + + + void discovery_completed_cb(struct bt_gatt_dm *dm, + void *context) + { + if (concreteReceiverInstance.has_value()) { + concreteReceiverInstance.value().get().discovery_completed_cb(dm,context); + } + } + + void discovery_service_not_found_cb(struct bt_conn *conn, + void *context) + { + if (concreteReceiverInstance.has_value()) { + concreteReceiverInstance.value().get().discovery_service_not_found_cb(conn,context); + } + } + + void discovery_error_found_cb(struct bt_conn *conn, + int err, + void *context) + { + if (concreteReceiverInstance.has_value()) { + concreteReceiverInstance.value().get().discovery_error_found_cb(conn,err,context); + } + } + + const struct bt_gatt_dm_cb discovery_cb = { + .completed = discovery_completed_cb, + .service_not_found = discovery_service_not_found_cb, + .error_found = discovery_error_found_cb, + }; + + /// \brief Returns singleton discovery callback struct reference + const struct bt_gatt_dm_cb* getDiscoveryCallbacks() { + return &discovery_cb; + } + + + // High level callbacks / access functions + + + uint8_t gatt_read_cb(struct bt_conn *conn, uint8_t err, + struct bt_gatt_read_params *params, + const void *data, uint16_t length) + { + if (concreteReceiverInstance.has_value()) { + return concreteReceiverInstance.value().get().gatt_read_cb(conn,err,params,data,length); + } + return length; // say we've consumed the data anyway + } + + + void scan_cb(const bt_addr_le_t *addr, int8_t rssi, uint8_t adv_type, + struct net_buf_simple *buf) { + if (concreteReceiverInstance.has_value()) { + concreteReceiverInstance.value().get().scan_cb(addr,rssi,adv_type,buf); + } + } + + +} + } } diff --git a/herald/src/ble/zephyr/concrete_ble_transmitter.cpp b/herald/src/ble/zephyr/concrete_ble_transmitter.cpp index b409faf..5f36d65 100644 --- a/herald/src/ble/zephyr/concrete_ble_transmitter.cpp +++ b/herald/src/ble/zephyr/concrete_ble_transmitter.cpp @@ -3,6 +3,7 @@ // #include "herald/ble/zephyr/concrete_ble_transmitter.h" +#include "herald/payload/payload_data_supplier.h" // nRF Connect SDK includes #include @@ -23,10 +24,113 @@ namespace ble { using namespace herald; using namespace herald::datatype; using namespace herald::data; +using namespace herald::payload; namespace zephyrinternal { + // FWD DECLS from concrete_ble_receiver + // struct bt_uuid_128* getHeraldUUID(); + // struct bt_uuid_128* getHeraldSignalAndroidCharUUID(); + // struct bt_uuid_128* getHeraldPayloadCharUUID(); + + /* Herald Service Variables */ + // struct bt_uuid_128 herald_uuid = BT_UUID_INIT_128( + // 0x9b, 0xfd, 0x5b, 0xd6, 0x72, 0x45, 0x1e, 0x80, 0xd3, 0x42, 0x46, 0x47, 0xaf, 0x32, 0x81, 0x42 + // ); + // struct bt_uuid_128 herald_char_signal_uuid = BT_UUID_INIT_128( + // 0x11, 0x1a, 0x82, 0x80, 0x9a, 0xe0, 0x24, 0x83, 0x7a, 0x43, 0x2e, 0x09, 0x13, 0xb8, 0x17, 0xf6 + // ); + // struct bt_uuid_128 herald_char_payload_uuid = BT_UUID_INIT_128( + // 0xe7, 0x33, 0x89, 0x8f, 0xe3, 0x43, 0x21, 0xa1, 0x29, 0x48, 0x05, 0x8f, 0xf8, 0xc0, 0x98, 0x3e + // ); + // Define kernel memory statically so we definitely have it + // struct bt_gatt_attr chrc_signal[2] = BT_GATT_CHARACTERISTIC(&zephyrinternal::getHeraldSignalAndroidCharUUID()->uuid, + // BT_GATT_CHRC_WRITE, + // BT_GATT_PERM_WRITE, + // zephyrinternal::read_vnd,zephyrinternal::write_vnd, nullptr); + // auto chrc_payload = BT_GATT_CHARACTERISTIC(&zephyrinternal::getHeraldPayloadCharUUID()->uuid, + // BT_GATT_CHRC_READ, + // BT_GATT_PERM_READ, + // zephyrinternal::read_payload, zephyrinternal::write_payload, nullptr); + // struct bt_gatt_attr attr_herald_svc_name[] = { + // BT_GATT_PRIMARY_SERVICE(getHeraldUUID()), + // chrc_signal, + // chrc_payload + // }; + // // const Z_STRUCT_SECTION_ITERABLE(bt_gatt_service_static, herald_svc) = + // // BT_GATT_SERVICE(attr_herald_svc_name); + // Z_DECL_ALIGN(struct bt_gatt_service_static) herald_svc __in_section(_bt_gatt_service_static, static, herald_svc) __used; + + struct bt_uuid_128 herald_uuid_tx = BT_UUID_INIT_128( + 0x9b, 0xfd, 0x5b, 0xd6, 0x72, 0x45, 0x1e, 0x80, 0xd3, 0x42, 0x46, 0x47, 0xaf, 0x32, 0x81, 0x42 + ); + struct bt_uuid_128 herald_char_signal_android_uuid_tx = BT_UUID_INIT_128( + 0x11, 0x1a, 0x82, 0x80, 0x9a, 0xe0, 0x24, 0x83, 0x7a, 0x43, 0x2e, 0x09, 0x13, 0xb8, 0x17, 0xf6 + ); + struct bt_uuid_128 herald_char_signal_ios_uuid_tx = BT_UUID_INIT_128( + 0x63, 0x43, 0x2d, 0xb0, 0xad, 0xa4, 0xf3, 0x8a, 0x9a, 0x4a, 0xe4, 0xea, 0xf2, 0xd5, 0xb0, 0x0e + ); + struct bt_uuid_128 herald_char_payload_uuid_tx = BT_UUID_INIT_128( + 0xe7, 0x33, 0x89, 0x8f, 0xe3, 0x43, 0x21, 0xa1, 0x29, 0x48, 0x05, 0x8f, 0xf8, 0xc0, 0x98, 0x3e + ); + BT_GATT_SERVICE_DEFINE(herald_svc, + BT_GATT_PRIMARY_SERVICE(&herald_uuid_tx), + BT_GATT_CHARACTERISTIC(&herald_char_signal_android_uuid_tx.uuid, + BT_GATT_CHRC_WRITE, + BT_GATT_PERM_WRITE, + zephyrinternal::read_vnd,zephyrinternal::write_vnd, nullptr), + BT_GATT_CHARACTERISTIC(&herald_char_payload_uuid_tx.uuid, + BT_GATT_CHRC_READ, + BT_GATT_PERM_READ, + zephyrinternal::read_payload, zephyrinternal::write_payload, nullptr) + ); + + + auto bp = BT_LE_ADV_CONN_NAME; // No TxPower support + /* + static auto bp = BT_LE_ADV_PARAM(BT_LE_ADV_OPT_CONNECTABLE | \ + BT_LE_ADV_OPT_USE_NAME, \ + BT_GAP_ADV_FAST_INT_MIN_2, \ + BT_GAP_ADV_FAST_INT_MAX_2, \ + BT_LE_ADV_OPT_USE_TX_POWER, \ + NULL); // txpower - REQUIRES EXT ADV OPT on Zephyr (experimental) + */ + struct bt_data ad[] = { + BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)), + // BT_DATA_BYTES(BT_DATA_TX_POWER, 0x00 ), // See https://github.com/vmware/herald-for-cpp/issues/26 + BT_DATA_BYTES(BT_DATA_UUID16_ALL, + BT_UUID_16_ENCODE(BT_UUID_DIS_VAL), + BT_UUID_16_ENCODE(BT_UUID_GATT_VAL), + BT_UUID_16_ENCODE(BT_UUID_GAP_VAL) + ), + BT_DATA_BYTES(BT_DATA_UUID128_ALL, + 0x9b, 0xfd, 0x5b, 0xd6, 0x72, 0x45, 0x1e, 0x80, + 0xd3, 0x42, 0x46, 0x47, 0xaf, 0x32, 0x81, 0x42 + ), + }; + + struct bt_data* getAdvertData() { + return ad; + } + std::size_t getAdvertDataSize() { + return ARRAY_SIZE(ad); + } + + struct bt_le_adv_param* getAdvertParams() { + return bp; + } + + PayloadDataSupplier* latestPds = NULL; + + PayloadDataSupplier* getPayloadDataSupplier() { + return latestPds; + } + + void setPayloadDataSupplier(PayloadDataSupplier* pds) { + latestPds = pds; + } + [[maybe_unused]] - static void get_tx_power(uint8_t handle_type, uint16_t handle, int8_t *tx_pwr_lvl) + void get_tx_power(uint8_t handle_type, uint16_t handle, int8_t *tx_pwr_lvl) { struct bt_hci_cp_vs_read_tx_power_level *cp; struct bt_hci_rp_vs_read_tx_power_level *rp; @@ -86,9 +190,9 @@ namespace zephyrinternal { // TODO bind the below to a class and control their values - static uint8_t vnd_value[] = { 'V', 'e', 'n', 'd', 'o', 'r' }; + uint8_t vnd_value[] = { 'V', 'e', 'n', 'd', 'o', 'r' }; - static ssize_t read_vnd(struct bt_conn *conn, const struct bt_gatt_attr *attr, + ssize_t read_vnd(struct bt_conn *conn, const struct bt_gatt_attr *attr, void *buf, uint16_t len, uint16_t offset) { const char *value = (const char*)attr->user_data; @@ -97,7 +201,7 @@ namespace zephyrinternal { strlen(value)); } - static ssize_t write_vnd(struct bt_conn *conn, const struct bt_gatt_attr *attr, + ssize_t write_vnd(struct bt_conn *conn, const struct bt_gatt_attr *attr, const void *buf, uint16_t len, uint16_t offset, uint8_t flags) { @@ -112,7 +216,7 @@ namespace zephyrinternal { return len; } - static ssize_t read_payload(struct bt_conn *conn, const struct bt_gatt_attr *attr, + ssize_t read_payload(struct bt_conn *conn, const struct bt_gatt_attr *attr, void *buf, uint16_t len, uint16_t offset) { const char *value = (const char*)attr->user_data; @@ -136,7 +240,7 @@ namespace zephyrinternal { strlen(value)); } - static ssize_t write_payload(struct bt_conn *conn, const struct bt_gatt_attr *attr, + ssize_t write_payload(struct bt_conn *conn, const struct bt_gatt_attr *attr, const void *buf, uint16_t len, uint16_t offset, uint8_t flags) { diff --git a/herald/src/data/zephyr/zephyr_logging_sink.cpp b/herald/src/data/zephyr/zephyr_logging_sink.cpp index 7c06f51..f1f9bad 100644 --- a/herald/src/data/zephyr/zephyr_logging_sink.cpp +++ b/herald/src/data/zephyr/zephyr_logging_sink.cpp @@ -4,9 +4,15 @@ #include "herald/data/zephyr/zephyr_logging_sink.h" +#include + #include namespace herald { + +// THE BELOW IS DONE IN EXACTLY ONE HERALD FILE +LOG_MODULE_REGISTER(heraldlogger, HERALD_LOG_LEVEL); + namespace data { void diff --git a/herald/src/datatype/signal_characteristic_data.cpp b/herald/src/datatype/signal_characteristic_data.cpp index 4fbccd2..a93d28e 100644 --- a/herald/src/datatype/signal_characteristic_data.cpp +++ b/herald/src/datatype/signal_characteristic_data.cpp @@ -38,18 +38,18 @@ int16(const Data& data, std::size_t index, bool& success) { // HEADER PUBLIC METHODS std::optional -encodeWriteRssi(const RSSI& rssi) noexcept { +encodeWriteRssi(const BLESensorConfiguration& config,const RSSI& rssi) noexcept { int r = rssi.intValue(); std::vector vec(3); - vec.push_back(BLESensorConfiguration::signalCharacteristicActionWriteRSSI); + vec.push_back(config.signalCharacteristicActionWriteRSSI); vec.push_back(std::byte(r)); // force least significant bit vec.push_back(std::byte(r >> 8)); // msb return std::optional(Data(std::move(vec))); // constructs the optional with a value } std::optional -decodeWriteRSSI(const Data& data) noexcept { - if (signalDataActionCode(data) != BLESensorConfiguration::signalCharacteristicActionWriteRSSI) { +decodeWriteRSSI(const BLESensorConfiguration& config,const Data& data) noexcept { + if (signalDataActionCode(data) != config.signalCharacteristicActionWriteRSSI) { return {}; } if (data.size() != 3) { @@ -64,10 +64,10 @@ decodeWriteRSSI(const Data& data) noexcept { } std::optional -encodeWritePayload(const PayloadData& payloadData) noexcept { +encodeWritePayload(const BLESensorConfiguration& config,const PayloadData& payloadData) noexcept { int r = (int)payloadData.size(); std::vector vec(3 + r); - vec.push_back(BLESensorConfiguration::signalCharacteristicActionWritePayload); + vec.push_back(config.signalCharacteristicActionWritePayload); vec.push_back(std::byte(r)); // force least significant bit vec.push_back(std::byte(r >> 8)); // msb Data d(std::move(vec)); @@ -76,8 +76,8 @@ encodeWritePayload(const PayloadData& payloadData) noexcept { } std::optional -decodeWritePayload(const Data& data) noexcept { - if (signalDataActionCode(data) != BLESensorConfiguration::signalCharacteristicActionWritePayload) { +decodeWritePayload(const BLESensorConfiguration& config,const Data& data) noexcept { + if (signalDataActionCode(data) != config.signalCharacteristicActionWritePayload) { return {}; } if (data.size() < 3) { @@ -88,18 +88,18 @@ decodeWritePayload(const Data& data) noexcept { if (!success) { return {}; } - if (data.size() != (3 + payloadDataCount)) { + if (data.size() != (3 + std::size_t(payloadDataCount))) { return {}; } return std::optional(PayloadData(data.subdata(3))); // constructs the optional with a value } std::optional -encodeWritePayloadSharing(const PayloadSharingData& payloadSharingData) noexcept { +encodeWritePayloadSharing(const BLESensorConfiguration& config,const PayloadSharingData& payloadSharingData) noexcept { int r = payloadSharingData.rssi.intValue(); int r2 = (int)payloadSharingData.data.size(); std::vector vec(5 + r2); - vec.push_back(BLESensorConfiguration::signalCharacteristicActionWritePayloadSharing); + vec.push_back(config.signalCharacteristicActionWritePayloadSharing); vec.push_back(std::byte(r)); // force least significant bit vec.push_back(std::byte(r >> 8)); // msb vec.push_back(std::byte(r2)); // force least significant bit @@ -110,8 +110,8 @@ encodeWritePayloadSharing(const PayloadSharingData& payloadSharingData) noexcept } std::optional -decodeWritePayloadSharing(const Data& data) noexcept { - if (signalDataActionCode(data) != BLESensorConfiguration::signalCharacteristicActionWritePayloadSharing) { +decodeWritePayloadSharing(const BLESensorConfiguration& config,const Data& data) noexcept { + if (signalDataActionCode(data) != config.signalCharacteristicActionWritePayloadSharing) { return {}; } if (data.size() < 5) { @@ -126,7 +126,7 @@ decodeWritePayloadSharing(const Data& data) noexcept { if (!success) { return {}; } - if (data.size() != (5 + payloadDataCount)) { + if (data.size() != (5 + std::size_t(payloadDataCount))) { return {}; } Data d = data.subdata(5); @@ -136,10 +136,10 @@ decodeWritePayloadSharing(const Data& data) noexcept { } std::optional -encodeImmediateSend(const ImmediateSendData& immediateSendData) noexcept { +encodeImmediateSend(const BLESensorConfiguration& config,const ImmediateSendData& immediateSendData) noexcept { int r = (int)immediateSendData.size(); std::vector vec(3 + r); - vec.push_back(BLESensorConfiguration::signalCharacteristicActionWriteImmediate); + vec.push_back(config.signalCharacteristicActionWriteImmediate); vec.push_back(static_cast(r)); // force least significant bit vec.push_back(static_cast(r >> 8)); // msb Data d(std::move(vec)); @@ -148,8 +148,8 @@ encodeImmediateSend(const ImmediateSendData& immediateSendData) noexcept { } std::optional -decodeImmediateSend(const Data& data) noexcept { - if (signalDataActionCode(data) != BLESensorConfiguration::signalCharacteristicActionWriteImmediate) { +decodeImmediateSend(const BLESensorConfiguration& config,const Data& data) noexcept { + if (signalDataActionCode(data) != config.signalCharacteristicActionWriteImmediate) { return {}; } if (data.size() < 3) { @@ -160,25 +160,25 @@ decodeImmediateSend(const Data& data) noexcept { if (!success) { return {}; } - if (data.size() != (3 + payloadDataCount)) { + if (data.size() != (3 + std::size_t(payloadDataCount))) { return {}; } return std::optional(ImmediateSendData(data.subdata(3))); // constructs the optional with a value } SignalCharacteristicDataType -detect(const Data& data) noexcept { - switch (signalDataActionCode(data)) { - case BLESensorConfiguration::signalCharacteristicActionWriteRSSI: - return SignalCharacteristicDataType::rssi; - case BLESensorConfiguration::signalCharacteristicActionWritePayload: - return SignalCharacteristicDataType::payload; - case BLESensorConfiguration::signalCharacteristicActionWritePayloadSharing: - return SignalCharacteristicDataType::payloadSharing; - case BLESensorConfiguration::signalCharacteristicActionWriteImmediate: - return SignalCharacteristicDataType::immediateSend; - default: - return SignalCharacteristicDataType::unknown; +detect(const BLESensorConfiguration& config,const Data& data) noexcept { + auto val = signalDataActionCode(data); + if (config.signalCharacteristicActionWriteRSSI == val) { + return SignalCharacteristicDataType::rssi; + } else if (config.signalCharacteristicActionWritePayload == val) { + return SignalCharacteristicDataType::payload; + } else if (config.signalCharacteristicActionWritePayloadSharing == val) { + return SignalCharacteristicDataType::payloadSharing; + } else if (config.signalCharacteristicActionWriteImmediate == val) { + return SignalCharacteristicDataType::immediateSend; + } else { + return SignalCharacteristicDataType::unknown; } } diff --git a/herald/src/zephyr_context.cpp b/herald/src/zephyr_context.cpp index f309005..1a54575 100644 --- a/herald/src/zephyr_context.cpp +++ b/herald/src/zephyr_context.cpp @@ -135,7 +135,7 @@ ZephyrContextProvider::getAdvertiser() noexcept int ZephyrContextProvider::enableBluetooth() noexcept { - LOG_INF("Context::enableBluetooth"); + // LOG_INF("Context::enableBluetooth"); int success; // TODO determine if default Zephyr mac address rotation uses Nordic CC3xx, if present @@ -164,13 +164,13 @@ ZephyrContextProvider::enableBluetooth() noexcept // return success; // } success = bt_enable(NULL); // NULL means synchronously enabled - LOG_INF("bt enable returned"); + // LOG_INF("bt enable returned"); // This should only called once if (IS_ENABLED(CONFIG_SETTINGS)) { - LOG_INF("Calling settings load"); + // LOG_INF("Calling settings load"); settings_load(); - LOG_INF("settings load returned"); + // LOG_INF("settings load returned"); } if (0 == success) { bluetoothEnabled = true; @@ -179,7 +179,7 @@ ZephyrContextProvider::enableBluetooth() noexcept delegate.get().bluetoothStateManager(BluetoothState::poweredOn); } } else { - LOG_INF("Error enabling Zephyr Bluetooth: %d", success); + // LOG_INF("Error enabling Zephyr Bluetooth: %d", success); } return success; From db77e60fbeedf1bbfadb6c6d105e596eae1fb533 Mon Sep 17 00:00:00 2001 From: Adam Fowler Date: Sat, 10 Apr 2021 19:45:55 +0100 Subject: [PATCH 23/28] Wearable app working on nRF5340DK Advertising and scanning working Known issues: - After a phone with an app has connected to the wearable, that phone cannot use nRF Connect app to introspect the service list via GATT (likely connection held in invalid state) Signed-off-by: Adam Fowler --- herald-wearable/.vscode/launch.json | 31 +++++++++++++++++++++- herald-wearable/.vscode/settings.json | 3 ++- herald-wearable/src/main.cpp | 1 + herald/include/herald/engine/coordinator.h | 6 ++--- herald/include/herald/sensor_array.h | 13 ++++----- 5 files changed, 43 insertions(+), 11 deletions(-) diff --git a/herald-wearable/.vscode/launch.json b/herald-wearable/.vscode/launch.json index 00a9d8a..7661093 100644 --- a/herald-wearable/.vscode/launch.json +++ b/herald-wearable/.vscode/launch.json @@ -4,6 +4,7 @@ // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 "version": "0.2.0", "configurations": [ + { "type": "gnu-debugger", "request": "launch", @@ -16,7 +17,35 @@ "server": "C:/Program Files (x86)/SEGGER/JLink/JLinkGDBServerCL.exe", }, "serverArgs": [ - //"-device", "NRF5340_XXAA_APP", + "-device", "NRF5340_XXAA_APP", + "-if", "SWD", + "-speed", "4000" + ], + "serverHost": "localhost", + "serverPort": 2331, + "customVariables": [ + "port0", + "port1", + "port2", + ], + "autoRun": false, + "debugOutput": false //, + //"preLaunchTask": "build firmware" + }, + + + { + "type": "gnu-debugger", + "request": "launch", + "name": "Debug - NRF52833_XXAA - Launch", + "program": "${workspaceFolder}/build/zephyr/zephyr.elf", + "toolchain": "${config:armToolchainPath}/bin", + "client": "arm-none-eabi-gdb.exe", + "server": "JLinkGDBServer", + "windows": { + "server": "C:/Program Files (x86)/SEGGER/JLink/JLinkGDBServerCL.exe", + }, + "serverArgs": [ "-device", "NRF52833_XXAA", "-if", "SWD", "-speed", "4000" diff --git a/herald-wearable/.vscode/settings.json b/herald-wearable/.vscode/settings.json index 37cf401..19e4596 100644 --- a/herald-wearable/.vscode/settings.json +++ b/herald-wearable/.vscode/settings.json @@ -63,6 +63,7 @@ "cinttypes": "cpp" }, "cmake.configureEnvironment": { - "BOARD": "nrf52833dk_nrf52833" + // "BOARD": "nrf52833dk_nrf52833" + "BOARD": "nrf5340dk_nrf5340_cpuapp" } } \ No newline at end of file diff --git a/herald-wearable/src/main.cpp b/herald-wearable/src/main.cpp index 1c0e9ed..0dff18d 100644 --- a/herald-wearable/src/main.cpp +++ b/herald-wearable/src/main.cpp @@ -294,6 +294,7 @@ void herald_entry() { // Create Herald sensor array - this handles both advertising (Transmitter) and scanning/connecting (Receiver) SensorArray sa(ctx,pds); ConcreteBLESensor ble(ctx,ctx.getBluetoothStateManager(),pds); + sa.add(ble); // Add contacts.log delegate // CURRENTLY BREAKS ZEPHYR - DON'T KNOW WHY YET - LOGGING SUBSYSTEM ISSUE diff --git a/herald/include/herald/engine/coordinator.h b/herald/include/herald/engine/coordinator.h index fc15525..bcfc54c 100644 --- a/herald/include/herald/engine/coordinator.h +++ b/herald/include/herald/engine/coordinator.h @@ -51,16 +51,16 @@ class Coordinator { ~Coordinator() = default; /// Introspect and include in iteration planning - void add(std::shared_ptr sensor) { + void add(Sensor& sensor) { HTDBG("Adding sensor"); - auto prov = sensor->coordinationProvider(); + auto prov = sensor.coordinationProvider(); if (prov.has_value()) { HTDBG("Sensor has Provider implementation"); providers.push_back(prov.value()); } } /// Remove from iteration planning - void remove(std::shared_ptr sensor) + void remove(Sensor& sensor) { // TODO support remove } diff --git a/herald/include/herald/sensor_array.h b/herald/include/herald/sensor_array.h index 73ea84d..48d29bc 100644 --- a/herald/include/herald/sensor_array.h +++ b/herald/include/herald/sensor_array.h @@ -21,6 +21,7 @@ #include #include #include +#include namespace herald { @@ -71,21 +72,21 @@ class SensorArray : public Sensor { } /// \brief Adds a new sensor to the array, and add its coordination provider to the engine - void add(const std::shared_ptr& sensor) { - mSensorArray.push_back(sensor); // adds in links to BLE transmitter, receiver + void add(Sensor& sensor) { + mSensorArray.emplace_back(sensor); // adds in links to BLE transmitter, receiver engine.add(sensor); } // SENSOR OVERRIDES void add(const std::shared_ptr& delegate) override { for (auto& sensor: mSensorArray) { - sensor->add(delegate); + sensor.get().add(delegate); } } void start() override { for (auto& sensor: mSensorArray) { - sensor->start(); + sensor.get().start(); } engine.start(); } @@ -93,7 +94,7 @@ class SensorArray : public Sensor { void stop() override { engine.stop(); for (auto& sensor: mSensorArray) { - sensor->stop(); + sensor.get().stop(); } } @@ -111,7 +112,7 @@ class SensorArray : public Sensor { // Initialised on entry to Impl constructor:- ContextT& mContext; std::shared_ptr mPayloadDataSupplier; - std::vector> mSensorArray; + std::vector> mSensorArray; Coordinator engine; From dd53c8f18df5ac8aa0707fa86b90ed1bb066dc24 Mon Sep 17 00:00:00 2001 From: Adam Fowler Date: Sat, 10 Apr 2021 19:59:00 +0100 Subject: [PATCH 24/28] nRF52833 working with larger heap size Used CONFIG_HEAP_MEM_POOL_SIZE=16384 Signed-off-by: Adam Fowler --- config/zephyr/nRF52833.conf | 2 +- herald-wearable/.vscode/settings.json | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/config/zephyr/nRF52833.conf b/config/zephyr/nRF52833.conf index fde1ec2..83048f4 100644 --- a/config/zephyr/nRF52833.conf +++ b/config/zephyr/nRF52833.conf @@ -18,4 +18,4 @@ CONFIG_TINYCRYPT_SHA256=y CONFIG_BT_MAX_CONN=10 # Fix for use of heap in gatt_gm erroring with undefined reference to k_aligned_alloc -CONFIG_HEAP_MEM_POOL_SIZE=256 \ No newline at end of file +CONFIG_HEAP_MEM_POOL_SIZE=16384 \ No newline at end of file diff --git a/herald-wearable/.vscode/settings.json b/herald-wearable/.vscode/settings.json index 19e4596..7f2e53b 100644 --- a/herald-wearable/.vscode/settings.json +++ b/herald-wearable/.vscode/settings.json @@ -63,7 +63,7 @@ "cinttypes": "cpp" }, "cmake.configureEnvironment": { - // "BOARD": "nrf52833dk_nrf52833" - "BOARD": "nrf5340dk_nrf5340_cpuapp" + "BOARD": "nrf52833dk_nrf52833" + // "BOARD": "nrf5340dk_nrf5340_cpuapp" } } \ No newline at end of file From cc5ce645410e19f74b79eda3f93b8dccc306af26 Mon Sep 17 00:00:00 2001 From: Adam Fowler Date: Sat, 10 Apr 2021 20:11:50 +0100 Subject: [PATCH 25/28] Building for all except nRF52832 due to SRAM use Working and tested on nRF52833, nRF52840, nRF5340 DKs Signed-off-by: Adam Fowler --- config/zephyr/nRF52832.conf | 4 +- config/zephyr/nRF52840.conf | 2 +- herald-wearable/.vscode/launch.json | 58 +++++++++++++++++++++++++++ herald-wearable/.vscode/settings.json | 4 +- herald-wearable/src/main.cpp | 2 +- 5 files changed, 65 insertions(+), 5 deletions(-) diff --git a/config/zephyr/nRF52832.conf b/config/zephyr/nRF52832.conf index 9895766..422be9e 100644 --- a/config/zephyr/nRF52832.conf +++ b/config/zephyr/nRF52832.conf @@ -15,7 +15,7 @@ CONFIG_TINYCRYPT_SHA256=y # Simple Payload Support ENDS #CONFIG_BT_MAX_CONN=20 -CONFIG_BT_MAX_CONN=5 +CONFIG_BT_MAX_CONN=3 # Fix for use of heap in gatt_gm erroring with undefined reference to k_aligned_alloc -CONFIG_HEAP_MEM_POOL_SIZE=256 \ No newline at end of file +CONFIG_HEAP_MEM_POOL_SIZE=8192 \ No newline at end of file diff --git a/config/zephyr/nRF52840.conf b/config/zephyr/nRF52840.conf index afcf350..8087266 100644 --- a/config/zephyr/nRF52840.conf +++ b/config/zephyr/nRF52840.conf @@ -23,4 +23,4 @@ CONFIG_TINYCRYPT_SHA256=y CONFIG_BT_MAX_CONN=10 # Fix for use of heap in gatt_gm erroring with undefined reference to k_aligned_alloc -CONFIG_HEAP_MEM_POOL_SIZE=256 \ No newline at end of file +CONFIG_HEAP_MEM_POOL_SIZE=16384 \ No newline at end of file diff --git a/herald-wearable/.vscode/launch.json b/herald-wearable/.vscode/launch.json index 7661093..b782312 100644 --- a/herald-wearable/.vscode/launch.json +++ b/herald-wearable/.vscode/launch.json @@ -34,6 +34,35 @@ }, + { + "type": "gnu-debugger", + "request": "launch", + "name": "Debug - NRF52840_XXAA - Launch", + "program": "${workspaceFolder}/build/zephyr/zephyr.elf", + "toolchain": "${config:armToolchainPath}/bin", + "client": "arm-none-eabi-gdb.exe", + "server": "JLinkGDBServer", + "windows": { + "server": "C:/Program Files (x86)/SEGGER/JLink/JLinkGDBServerCL.exe", + }, + "serverArgs": [ + "-device", "NRF52840_XXAA", + "-if", "SWD", + "-speed", "4000" + ], + "serverHost": "localhost", + "serverPort": 2331, + "customVariables": [ + "port0", + "port1", + "port2", + ], + "autoRun": false, + "debugOutput": false //, + //"preLaunchTask": "build firmware" + }, + + { "type": "gnu-debugger", "request": "launch", @@ -60,6 +89,35 @@ "autoRun": false, "debugOutput": false //, //"preLaunchTask": "build firmware" + }, + + + { + "type": "gnu-debugger", + "request": "launch", + "name": "Debug - NRF52832_XXAA - Launch", + "program": "${workspaceFolder}/build/zephyr/zephyr.elf", + "toolchain": "${config:armToolchainPath}/bin", + "client": "arm-none-eabi-gdb.exe", + "server": "JLinkGDBServer", + "windows": { + "server": "C:/Program Files (x86)/SEGGER/JLink/JLinkGDBServerCL.exe", + }, + "serverArgs": [ + "-device", "NRF52832_XXAA", + "-if", "SWD", + "-speed", "4000" + ], + "serverHost": "localhost", + "serverPort": 2331, + "customVariables": [ + "port0", + "port1", + "port2", + ], + "autoRun": false, + "debugOutput": false //, + //"preLaunchTask": "build firmware" } ] } \ No newline at end of file diff --git a/herald-wearable/.vscode/settings.json b/herald-wearable/.vscode/settings.json index 7f2e53b..e0aee63 100644 --- a/herald-wearable/.vscode/settings.json +++ b/herald-wearable/.vscode/settings.json @@ -63,7 +63,9 @@ "cinttypes": "cpp" }, "cmake.configureEnvironment": { - "BOARD": "nrf52833dk_nrf52833" + "BOARD": "nrf52dk_nrf52832" + // "BOARD": "nrf52833dk_nrf52833" + // "BOARD": "nrf52840dk_nrf52840" // "BOARD": "nrf5340dk_nrf5340_cpuapp" } } \ No newline at end of file diff --git a/herald-wearable/src/main.cpp b/herald-wearable/src/main.cpp index 0dff18d..5260b18 100644 --- a/herald-wearable/src/main.cpp +++ b/herald-wearable/src/main.cpp @@ -68,7 +68,7 @@ #endif struct k_thread herald_thread; -K_THREAD_STACK_DEFINE(herald_stack, 9192); // TODO reduce this down +K_THREAD_STACK_DEFINE(herald_stack, 2048); // Was 9192 using namespace herald; using namespace herald::data; From b22d4f865f5884a3a26084d05b28fbdf688655cd Mon Sep 17 00:00:00 2001 From: Adam Fowler Date: Sat, 10 Apr 2021 20:16:51 +0100 Subject: [PATCH 26/28] All tests passing on Windows With new reference based sensor usage Signed-off-by: Adam Fowler --- herald-tests/blecoordinator-tests.cpp | 12 ++++++------ herald-tests/coordinator-tests.cpp | 5 +++-- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/herald-tests/blecoordinator-tests.cpp b/herald-tests/blecoordinator-tests.cpp index ffe0d3a..c05814d 100644 --- a/herald-tests/blecoordinator-tests.cpp +++ b/herald-tests/blecoordinator-tests.cpp @@ -190,7 +190,7 @@ TEST_CASE("blecoordinator-android-no-id", "[coordinator][android-no-id][basic]") // Specify that some activity has already happened with the device std::vector heraldServiceList; - heraldServiceList.push_back(herald::ble::BLESensorConfiguration::serviceUUID); + heraldServiceList.push_back(ctx.getSensorConfiguration().serviceUUID); devPtr1->services(heraldServiceList); devPtr1->operatingSystem(herald::ble::BLEDeviceOperatingSystem::android); @@ -232,7 +232,7 @@ TEST_CASE("blecoordinator-two-mixed-no-id", "[coordinator][two-mixed-no-id][basi // Specify that some activity has already happened with the device std::vector heraldServiceList; - heraldServiceList.push_back(herald::ble::BLESensorConfiguration::serviceUUID); + heraldServiceList.push_back(ctx.getSensorConfiguration().serviceUUID); devPtr1->services(heraldServiceList); devPtr1->operatingSystem(herald::ble::BLEDeviceOperatingSystem::android); devPtr2->services(heraldServiceList); @@ -272,7 +272,7 @@ TEST_CASE("blecoordinator-got-os-and-id", "[coordinator][got-os-and-id][basic]") // Specify that some activity has already happened with the device std::vector heraldServiceList; - heraldServiceList.push_back(herald::ble::BLESensorConfiguration::serviceUUID); + heraldServiceList.push_back(ctx.getSensorConfiguration().serviceUUID); devPtr1->services(heraldServiceList); devPtr1->operatingSystem(herald::ble::BLEDeviceOperatingSystem::android); devPtr1->payloadData(herald::datatype::PayloadData(std::byte(5),32)); @@ -308,7 +308,7 @@ TEST_CASE("blecoordinator-got-two-at-different-states", "[coordinator][got-two-a // Specify that some activity has already happened with the device std::vector heraldServiceList; - heraldServiceList.push_back(herald::ble::BLESensorConfiguration::serviceUUID); + heraldServiceList.push_back(ctx.getSensorConfiguration().serviceUUID); devPtr1->services(heraldServiceList); devPtr1->payloadData(herald::datatype::PayloadData(std::byte(5),32)); devPtr2->services(heraldServiceList); @@ -348,7 +348,7 @@ TEST_CASE("blecoordinator-got-immediate-send-targeted", "[coordinator][got-immed // Specify that some activity has already happened with the device std::vector heraldServiceList; - heraldServiceList.push_back(herald::ble::BLESensorConfiguration::serviceUUID); + heraldServiceList.push_back(ctx.getSensorConfiguration().serviceUUID); devPtr1->services(heraldServiceList); devPtr1->operatingSystem(herald::ble::BLEDeviceOperatingSystem::android); devPtr1->payloadData(herald::datatype::PayloadData(std::byte(5),32)); @@ -390,7 +390,7 @@ TEST_CASE("blecoordinator-got-three-at-different-states", "[coordinator][got-thr // Specify that some activity has already happened with the device std::vector heraldServiceList; - heraldServiceList.push_back(herald::ble::BLESensorConfiguration::serviceUUID); + heraldServiceList.push_back(ctx.getSensorConfiguration().serviceUUID); devPtr1->services(heraldServiceList); devPtr1->operatingSystem(herald::ble::BLEDeviceOperatingSystem::android); devPtr1->payloadData(herald::datatype::PayloadData(std::byte(5),32)); diff --git a/herald-tests/coordinator-tests.cpp b/herald-tests/coordinator-tests.cpp index bf2488d..177323f 100644 --- a/herald-tests/coordinator-tests.cpp +++ b/herald-tests/coordinator-tests.cpp @@ -181,7 +181,8 @@ class MockHeraldV1ProtocolProvider : public herald::ble::HeraldProtocolV1Provide HTDBG("serviceDiscovery called"); auto device = db.device(std::get<1>(act.prerequisites.front()).value()); std::vector heraldServiceList; - heraldServiceList.push_back(herald::ble::BLESensorConfiguration::serviceUUID); + herald::ble::BLESensorConfiguration cfg; + heraldServiceList.push_back(cfg.serviceUUID); device->services(heraldServiceList); device->operatingSystem(herald::ble::BLEDeviceOperatingSystem::android); hasIdentifiedOs = true; @@ -254,7 +255,7 @@ TEST_CASE("coordinator-complex-iterations", "[coordinator][iterations][complex]" >; // Mock Sensor - std::shared_ptr> mockSensor = std::make_shared>(coord); + MockSensor mockSensor(coord); // register ble coordinator herald::engine::Coordinator c(ctx); From d70281553a83c05e364c6c9d8599939f8b8e9c26 Mon Sep 17 00:00:00 2001 From: Adam Fowler Date: Sat, 10 Apr 2021 22:45:50 +0100 Subject: [PATCH 27/28] Venue beacon and wearable samples working On all but nRF52833 Also works now when transmitter disabled in app build All tests pass Signed-off-by: Adam Fowler --- herald-tests-zephyr/.vscode/settings.json | 6 + herald-venue-beacon/.vscode/settings.json | 6 + herald-venue-beacon/src/main.cpp | 15 +- herald-wearable/.vscode/settings.json | 4 +- herald/herald.cmake | 12 +- herald/include/herald.h | 4 + herald/include/herald/ble/ble_concrete.h | 4 + .../ble/default/concrete_ble_receiver.h | 29 +++ herald/include/herald/zephyr_context.h | 4 +- .../src/ble/default/concrete_ble_receiver.cpp | 210 +++++++++--------- herald/src/zephyr_context.cpp | 2 +- 11 files changed, 182 insertions(+), 114 deletions(-) diff --git a/herald-tests-zephyr/.vscode/settings.json b/herald-tests-zephyr/.vscode/settings.json index 36b6d5d..d6a4595 100644 --- a/herald-tests-zephyr/.vscode/settings.json +++ b/herald-tests-zephyr/.vscode/settings.json @@ -23,5 +23,11 @@ "xstddef": "cpp", "xtr1common": "cpp", "xutility": "cpp" + }, + "cmake.configureEnvironment": { + //"BOARD": "nrf52dk_nrf52832" + // "BOARD": "nrf52833dk_nrf52833" + // "BOARD": "nrf52840dk_nrf52840" + "BOARD": "nrf5340dk_nrf5340_cpuapp" } } \ No newline at end of file diff --git a/herald-venue-beacon/.vscode/settings.json b/herald-venue-beacon/.vscode/settings.json index 36b6d5d..d6a4595 100644 --- a/herald-venue-beacon/.vscode/settings.json +++ b/herald-venue-beacon/.vscode/settings.json @@ -23,5 +23,11 @@ "xstddef": "cpp", "xtr1common": "cpp", "xutility": "cpp" + }, + "cmake.configureEnvironment": { + //"BOARD": "nrf52dk_nrf52832" + // "BOARD": "nrf52833dk_nrf52833" + // "BOARD": "nrf52840dk_nrf52840" + "BOARD": "nrf5340dk_nrf5340_cpuapp" } } \ No newline at end of file diff --git a/herald-venue-beacon/src/main.cpp b/herald-venue-beacon/src/main.cpp index f8e88c6..ba5e5e1 100644 --- a/herald-venue-beacon/src/main.cpp +++ b/herald-venue-beacon/src/main.cpp @@ -103,11 +103,16 @@ void main(void) using namespace herald::payload::beacon; using namespace herald::payload::extended; + // Create Herald sensor array + ZephyrContextProvider zcp; + Context ctx(zcp,zcp.getLoggingSink(),zcp.getBluetoothStateManager()); + using CT = Context; + // Disable receiver / scanning mode - we're just transmitting our value - herald::ble::BLESensorConfiguration::scanningEnabled = false; + BLESensorConfiguration config = ctx.getSensorConfiguration(); // copy ctor + config.scanningEnabled = false; + ctx.setSensorConfiguration(config); - // Create Herald sensor array - std::shared_ptr ctx = std::make_shared(); ConcreteExtendedDataV1 extendedData; extendedData.addSection(ExtendedDataSegmentCodesV1::TextPremises, erinsStakehouse.name); @@ -117,7 +122,11 @@ void main(void) erinsStakehouse.code, extendedData ); + SensorArray sa(ctx,pds); + ConcreteBLESensor ble(ctx,ctx.getBluetoothStateManager(),pds); + sa.add(ble); + // Start array (and thus start advertising) sa.start(); diff --git a/herald-wearable/.vscode/settings.json b/herald-wearable/.vscode/settings.json index e0aee63..65f728d 100644 --- a/herald-wearable/.vscode/settings.json +++ b/herald-wearable/.vscode/settings.json @@ -63,9 +63,9 @@ "cinttypes": "cpp" }, "cmake.configureEnvironment": { - "BOARD": "nrf52dk_nrf52832" + // "BOARD": "nrf52dk_nrf52832" // "BOARD": "nrf52833dk_nrf52833" // "BOARD": "nrf52840dk_nrf52840" - // "BOARD": "nrf5340dk_nrf5340_cpuapp" + "BOARD": "nrf5340dk_nrf5340_cpuapp" } } \ No newline at end of file diff --git a/herald/herald.cmake b/herald/herald.cmake index d5584c9..5f36e51 100644 --- a/herald/herald.cmake +++ b/herald/herald.cmake @@ -79,11 +79,21 @@ set(HERALD_HEADERS ) set(HERALD_HEADERS_ZEPHYR - ${HERALD_BASE}/include/herald/ble/zephyr/concrete_ble_receiver.h ${HERALD_BASE}/include/herald/ble/zephyr/concrete_ble_transmitter.h ${HERALD_BASE}/include/herald/data/zephyr/zephyr_logging_sink.h ${HERALD_BASE}/include/herald/zephyr_context.h ) +if(DEFINED CONFIG_BT_SCAN) + set(HERALD_HEADERS_ZEPHYR + ${HERALD_HEADERS_ZEPHYR} + ${HERALD_BASE}/include/herald/ble/zephyr/concrete_ble_receiver.h + ) +else() + set(HERALD_HEADERS_ZEPHYR + ${HERALD_HEADERS_ZEPHYR} + ${HERALD_BASE}/include/herald/ble/default/concrete_ble_receiver.h + ) +endif() set(HERALD_HEADERS_WINDOWS ) diff --git a/herald/include/herald.h b/herald/include/herald.h index 67835d0..75170b6 100644 --- a/herald/include/herald.h +++ b/herald/include/herald.h @@ -23,7 +23,11 @@ #include "herald/zephyr_context.h" #include "herald/data/zephyr/zephyr_logging_sink.h" #include "herald/ble/zephyr/concrete_ble_transmitter.h" +#ifdef CONFIG_BT_SCAN #include "herald/ble/zephyr/concrete_ble_receiver.h" +#else +#include "herald/ble/default/concrete_ble_receiver.h" +#endif #endif // Datatype namespace diff --git a/herald/include/herald/ble/ble_concrete.h b/herald/include/herald/ble/ble_concrete.h index d2bb050..6e2d2ee 100644 --- a/herald/include/herald/ble/ble_concrete.h +++ b/herald/include/herald/ble/ble_concrete.h @@ -24,7 +24,11 @@ // Include the relevant concrete BLE Receiver here #ifdef __ZEPHYR__ +#ifdef CONFIG_BT_SCAN #include "zephyr/concrete_ble_receiver.h" +#else +#include "default/concrete_ble_receiver.h" +#endif #include "zephyr/concrete_ble_transmitter.h" // TODO other platforms here #else diff --git a/herald/include/herald/ble/default/concrete_ble_receiver.h b/herald/include/herald/ble/default/concrete_ble_receiver.h index 6f11011..abfb304 100644 --- a/herald/include/herald/ble/default/concrete_ble_receiver.h +++ b/herald/include/herald/ble/default/concrete_ble_receiver.h @@ -46,6 +46,35 @@ class ConcreteBLEReceiver : public BLEReceiver, public HeraldProtocolV1Provider void start() override {} void stop() override {} + // Herald V1 Protocol Provider methods + bool openConnection(const TargetIdentifier& toTarget) override { + return false; + } + + bool closeConnection(const TargetIdentifier& toTarget) override { + return true; + } + + void restartScanningAndAdvertising() override { + ; + } + + std::optional serviceDiscovery(Activity) override { + return {}; + } + + std::optional readPayload(Activity) override { + return {}; + } + + std::optional immediateSend(Activity) override { + return {}; + } + + std::optional immediateSendAll(Activity) override { + return {}; + } + }; } diff --git a/herald/include/herald/zephyr_context.h b/herald/include/herald/zephyr_context.h index a4db221..4dd39f3 100644 --- a/herald/include/herald/zephyr_context.h +++ b/herald/include/herald/zephyr_context.h @@ -69,7 +69,7 @@ class ZephyrContextProvider : BluetoothStateManager { // Zephyr OS specific methods int enableBluetooth() noexcept; - zephyrinternal::Advertiser& getAdvertiser() noexcept; + herald::zephyrinternal::Advertiser& getAdvertiser() noexcept; int startBluetooth() noexcept; int stopBluetooth() noexcept; @@ -79,7 +79,7 @@ class ZephyrContextProvider : BluetoothStateManager { private: ZephyrLoggingSink sink; - zephyrinternal::Advertiser advertiser; + herald::zephyrinternal::Advertiser advertiser; std::vector> stateDelegates; diff --git a/herald/src/ble/default/concrete_ble_receiver.cpp b/herald/src/ble/default/concrete_ble_receiver.cpp index 0c92a1f..8a5b0fc 100644 --- a/herald/src/ble/default/concrete_ble_receiver.cpp +++ b/herald/src/ble/default/concrete_ble_receiver.cpp @@ -13,111 +13,111 @@ namespace ble { using namespace herald::datatype; using namespace herald::data; -/** - * Dummy BLE Receiver to allow compilation on Zephyr and other platforms - * when Receiver code (E.g. OS scan libraries) have been compiled out. - * See herald.cmake for usage. - */ - -class ConcreteBLEReceiver::Impl { -public: - Impl() = default; - ~Impl() = default; -}; - - - -ConcreteBLEReceiver::ConcreteBLEReceiver(std::shared_ptr ctx, std::shared_ptr bluetoothStateManager, - std::shared_ptr payloadDataSupplier, std::shared_ptr bleDatabase) - : mImpl(std::make_unique()) -{ - ; -} - -ConcreteBLEReceiver::~ConcreteBLEReceiver() = default; - - -// Coordination overrides - Since v1.2-beta3 -std::optional> -ConcreteBLEReceiver::coordinationProvider() -{ - return {}; -} - -bool -ConcreteBLEReceiver::immediateSend(Data data, const TargetIdentifier& targetIdentifier) -{ - return false; -} - -bool -ConcreteBLEReceiver::immediateSendAll(Data data) -{ - return false; -} - - -// Sensor overrides -void -ConcreteBLEReceiver::add(const std::shared_ptr& delegate) -{ - ; -} - -void -ConcreteBLEReceiver::start() -{ - ; -} - -void -ConcreteBLEReceiver::stop() -{ - ; -} - - -bool -ConcreteBLEReceiver::openConnection(const TargetIdentifier& toTarget) -{ - return false; -} - -bool -ConcreteBLEReceiver::closeConnection(const TargetIdentifier& toTarget) -{ - return false; -} - -void -ConcreteBLEReceiver::restartScanningAndAdvertising() -{ - ; -} - -std::optional -ConcreteBLEReceiver::serviceDiscovery(Activity) -{ - return {}; -} - -std::optional -ConcreteBLEReceiver::readPayload(Activity) -{ - return {}; -} - -std::optional -ConcreteBLEReceiver::immediateSend(Activity) -{ - return {}; -} - -std::optional -ConcreteBLEReceiver::immediateSendAll(Activity) -{ - return {}; -} +// /** +// * Dummy BLE Receiver to allow compilation on Zephyr and other platforms +// * when Receiver code (E.g. OS scan libraries) have been compiled out. +// * See herald.cmake for usage. +// */ + +// class ConcreteBLEReceiver::Impl { +// public: +// Impl() = default; +// ~Impl() = default; +// }; + + + +// ConcreteBLEReceiver::ConcreteBLEReceiver(std::shared_ptr ctx, std::shared_ptr bluetoothStateManager, +// std::shared_ptr payloadDataSupplier, std::shared_ptr bleDatabase) +// : mImpl(std::make_unique()) +// { +// ; +// } + +// ConcreteBLEReceiver::~ConcreteBLEReceiver() = default; + + +// // Coordination overrides - Since v1.2-beta3 +// std::optional> +// ConcreteBLEReceiver::coordinationProvider() +// { +// return {}; +// } + +// bool +// ConcreteBLEReceiver::immediateSend(Data data, const TargetIdentifier& targetIdentifier) +// { +// return false; +// } + +// bool +// ConcreteBLEReceiver::immediateSendAll(Data data) +// { +// return false; +// } + + +// // Sensor overrides +// void +// ConcreteBLEReceiver::add(const std::shared_ptr& delegate) +// { +// ; +// } + +// void +// ConcreteBLEReceiver::start() +// { +// ; +// } + +// void +// ConcreteBLEReceiver::stop() +// { +// ; +// } + + +// bool +// ConcreteBLEReceiver::openConnection(const TargetIdentifier& toTarget) +// { +// return false; +// } + +// bool +// ConcreteBLEReceiver::closeConnection(const TargetIdentifier& toTarget) +// { +// return false; +// } + +// void +// ConcreteBLEReceiver::restartScanningAndAdvertising() +// { +// ; +// } + +// std::optional +// ConcreteBLEReceiver::serviceDiscovery(Activity) +// { +// return {}; +// } + +// std::optional +// ConcreteBLEReceiver::readPayload(Activity) +// { +// return {}; +// } + +// std::optional +// ConcreteBLEReceiver::immediateSend(Activity) +// { +// return {}; +// } + +// std::optional +// ConcreteBLEReceiver::immediateSendAll(Activity) +// { +// return {}; +// } } } diff --git a/herald/src/zephyr_context.cpp b/herald/src/zephyr_context.cpp index 1a54575..1d9fcb1 100644 --- a/herald/src/zephyr_context.cpp +++ b/herald/src/zephyr_context.cpp @@ -126,7 +126,7 @@ ZephyrContextProvider::state() } } -zephyrinternal::Advertiser& +herald::zephyrinternal::Advertiser& ZephyrContextProvider::getAdvertiser() noexcept { return advertiser; From ef647effbdeb0df092994fc3075efc6e786f807a Mon Sep 17 00:00:00 2001 From: Adam Fowler Date: Mon, 12 Apr 2021 08:16:25 +0100 Subject: [PATCH 28/28] Configuration for Beta release Known issues:- - Logging Analysis Delegate needs work - Need to enable analysis in wearable main sample Signed-off-by: Adam Fowler --- herald-wearable/.vscode/settings.json | 1 + herald-wearable/src/main.cpp | 14 ++++++++++---- .../herald/analysis/logging_analysis_delegate.h | 17 ++++++++++++----- 3 files changed, 23 insertions(+), 9 deletions(-) diff --git a/herald-wearable/.vscode/settings.json b/herald-wearable/.vscode/settings.json index 65f728d..01bbc3f 100644 --- a/herald-wearable/.vscode/settings.json +++ b/herald-wearable/.vscode/settings.json @@ -67,5 +67,6 @@ // "BOARD": "nrf52833dk_nrf52833" // "BOARD": "nrf52840dk_nrf52840" "BOARD": "nrf5340dk_nrf5340_cpuapp" + // ,"HERALD_ANALYSIS_ENABLED":"y" } } \ No newline at end of file diff --git a/herald-wearable/src/main.cpp b/herald-wearable/src/main.cpp index 5260b18..259060d 100644 --- a/herald-wearable/src/main.cpp +++ b/herald-wearable/src/main.cpp @@ -68,7 +68,13 @@ #endif struct k_thread herald_thread; -K_THREAD_STACK_DEFINE(herald_stack, 2048); // Was 9192 +K_THREAD_STACK_DEFINE(herald_stack, +#ifdef CONFIG_BT_MAX_CONN + 1024 + (CONFIG_BT_MAX_CONN * 768) +#else + 9192 +#endif +); // Was 9192 for nRF5340 (10 conns), 2048 for nRF52832 (3 conns) using namespace herald; using namespace herald::data; @@ -313,18 +319,18 @@ void herald_entry() { #ifdef HERALD_ANALYSIS_ENABLED herald::analysis::algorithms::distance::FowlerBasicAnalyser distanceAnalyser(0, -50, -24); // 0 = run every time run() is called - herald::analysis::LoggingAnalysisDelegate myDelegate(ctx); + herald::analysis::LoggingAnalysisDelegate myDelegate(ctx); herald::analysis::AnalysisDelegateManager adm(std::move(myDelegate)); // NOTE: myDelegate MOVED FROM and no longer accessible herald::analysis::AnalysisProviderManager apm(std::move(distanceAnalyser)); // NOTE: distanceAnalyser MOVED FROM and no longer accessible herald::analysis::AnalysisRunner< - herald::analysis::AnalysisDelegateManager>, + herald::analysis::AnalysisDelegateManager>, herald::analysis::AnalysisProviderManager, RSSI,Distance > runner(adm, apm); // just for Sample types, and their produced output (Sample) std::shared_ptr> src = std::make_shared>(runner); - sa->add(src); + sa.add(src); #endif diff --git a/herald/include/herald/analysis/logging_analysis_delegate.h b/herald/include/herald/analysis/logging_analysis_delegate.h index c03913e..c08f117 100644 --- a/herald/include/herald/analysis/logging_analysis_delegate.h +++ b/herald/include/herald/analysis/logging_analysis_delegate.h @@ -10,6 +10,8 @@ #include "../context.h" #include +#include +#include namespace herald { namespace analysis { @@ -27,17 +29,22 @@ struct LoggingAnalysisDelegate { // HLOGGERINIT(nullptr,"herald","LoggingAnalysisDelegate") // { // } + LoggingAnalysisDelegate() + : ctx() + // HLOGGERINIT(ctx,"herald","LoggingAnalysisDelegate") + { + } - LoggingAnalysisDelegate(ContextT& ctx) - : ctx(ctx) - HLOGGERINIT(ctx,"herald","LoggingAnalysisDelegate") + LoggingAnalysisDelegate(ContextT& context) + : ctx(context) + HLOGGERINIT(ctx.value().get(),"herald","LoggingAnalysisDelegate") { } LoggingAnalysisDelegate(const LoggingAnalysisDelegate&) = delete; // copy ctor deleted LoggingAnalysisDelegate(LoggingAnalysisDelegate&& other) noexcept : ctx(other.ctx) - HLOGGERINIT(ctx,"herald","LoggingAnalysisDelegate") + HLOGGERINIT(ctx.value().get(),"herald","LoggingAnalysisDelegate") { } // move ctor @@ -57,7 +64,7 @@ struct LoggingAnalysisDelegate { } private: - ContextT& ctx; + std::optional> ctx; HLOGGER(ContextT); };