ruble 0.0.3.alpha

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (92) hide show
  1. checksums.yaml +7 -0
  2. data/.gdbinit +21 -0
  3. data/.gitignore +18 -0
  4. data/.rubocop.yml +96 -0
  5. data/.ruby-version +1 -0
  6. data/CHANGELOG.md +6 -0
  7. data/CMakeLists.txt +4 -0
  8. data/CODE_OF_CONDUCT.md +84 -0
  9. data/Gemfile +21 -0
  10. data/Gemfile.lock +98 -0
  11. data/LICENSE.txt +21 -0
  12. data/README.md +63 -0
  13. data/Rakefile +41 -0
  14. data/ext/ruble/.gitignore +5 -0
  15. data/ext/ruble/CMakeLists.txt +157 -0
  16. data/ext/ruble/RuBLEHelpers.cmake +240 -0
  17. data/ext/ruble/bindings/Adapter.cpp +193 -0
  18. data/ext/ruble/bindings/Adapter.hpp +85 -0
  19. data/ext/ruble/bindings/Characteristic.cpp +171 -0
  20. data/ext/ruble/bindings/Characteristic.hpp +132 -0
  21. data/ext/ruble/bindings/Descriptor.cpp +34 -0
  22. data/ext/ruble/bindings/Descriptor.hpp +69 -0
  23. data/ext/ruble/bindings/Peripheral.cpp +212 -0
  24. data/ext/ruble/bindings/Peripheral.hpp +108 -0
  25. data/ext/ruble/bindings/RuBLE.cpp +115 -0
  26. data/ext/ruble/bindings/Service.cpp +112 -0
  27. data/ext/ruble/bindings/Service.hpp +61 -0
  28. data/ext/ruble/bindings/common.hpp +48 -0
  29. data/ext/ruble/bindings/globals.cpp +43 -0
  30. data/ext/ruble/cmake.mk +62 -0
  31. data/ext/ruble/concerns/CharacteristicValueTracker.cpp +18 -0
  32. data/ext/ruble/concerns/CharacteristicValueTracker.hpp +40 -0
  33. data/ext/ruble/concerns/Rubyable.cpp +4 -0
  34. data/ext/ruble/concerns/Rubyable.hpp +46 -0
  35. data/ext/ruble/config.h.in +25 -0
  36. data/ext/ruble/containers/ByteArray.cpp +64 -0
  37. data/ext/ruble/containers/ByteArray.hpp +161 -0
  38. data/ext/ruble/containers/Callback.hpp +52 -0
  39. data/ext/ruble/containers/NamedBitSet.hpp +140 -0
  40. data/ext/ruble/containers/NamedBitSet.ipp +71 -0
  41. data/ext/ruble/extconf.rb +30 -0
  42. data/ext/ruble/management/Registry.cpp +63 -0
  43. data/ext/ruble/management/Registry.hpp +170 -0
  44. data/ext/ruble/management/RegistryFactory.hpp +113 -0
  45. data/ext/ruble/management/RubyQueue.cpp +152 -0
  46. data/ext/ruble/management/RubyQueue.hpp +69 -0
  47. data/ext/ruble/modularize.diff +28 -0
  48. data/ext/ruble/types/SimpleBLE.hpp +21 -0
  49. data/ext/ruble/types/declarations.hpp +91 -0
  50. data/ext/ruble/types/helpers.hpp +12 -0
  51. data/ext/ruble/types/ruby.hpp +36 -0
  52. data/ext/ruble/types/stl.hpp +41 -0
  53. data/ext/ruble/utils/RubyCallbackTraits.cpp +28 -0
  54. data/ext/ruble/utils/RubyCallbackTraits.hpp +48 -0
  55. data/ext/ruble/utils/async.cpp +10 -0
  56. data/ext/ruble/utils/async.hpp +76 -0
  57. data/ext/ruble/utils/containers.hpp +41 -0
  58. data/ext/ruble/utils/exception_handling.cpp +50 -0
  59. data/ext/ruble/utils/exception_handling.hpp +53 -0
  60. data/ext/ruble/utils/garbage_collection.cpp +82 -0
  61. data/ext/ruble/utils/garbage_collection.hpp +22 -0
  62. data/ext/ruble/utils/hash.cpp +83 -0
  63. data/ext/ruble/utils/hash.hpp +52 -0
  64. data/ext/ruble/utils/hexadecimal.hpp +116 -0
  65. data/ext/ruble/utils/human_type_names.hpp +38 -0
  66. data/ext/ruble/utils/inspection.cpp +24 -0
  67. data/ext/ruble/utils/inspection.hpp +108 -0
  68. data/ext/ruble/utils/ruby.hpp +103 -0
  69. data/ext/ruble/utils/ruby_context.hpp +73 -0
  70. data/lib/ruble/build/.rubocop.yml +19 -0
  71. data/lib/ruble/build/boost.rb +34 -0
  72. data/lib/ruble/build/cmake.rb +134 -0
  73. data/lib/ruble/build/core_ext.rb +5 -0
  74. data/lib/ruble/build/data/bundler.rb +24 -0
  75. data/lib/ruble/build/data/extension.rb +101 -0
  76. data/lib/ruble/build/data/os.rb +21 -0
  77. data/lib/ruble/build/data/rice.rb +24 -0
  78. data/lib/ruble/build/data.rb +22 -0
  79. data/lib/ruble/build/extconf.rb +76 -0
  80. data/lib/ruble/build/github_repo.rb +129 -0
  81. data/lib/ruble/build/simpleble.rb +56 -0
  82. data/lib/ruble/build.rb +28 -0
  83. data/lib/ruble/version.rb +7 -0
  84. data/lib/ruble.rb +46 -0
  85. data/lib/tasks/dev/dev_tasks.rb +130 -0
  86. data/lib/tasks/dev/pager.rb +218 -0
  87. data/lib/tasks/dev/paths.rb +30 -0
  88. data/lib/tasks/dev/state_hash.rb +65 -0
  89. data/lib/tasks/dev.rake +41 -0
  90. data/lib/tasks/simpleble.rake +29 -0
  91. data/sig/rubble.rbs +4 -0
  92. metadata +263 -0
@@ -0,0 +1,108 @@
1
+ #pragma once
2
+
3
+ #include "containers/Callback.hpp"
4
+ #include "containers/NamedBitSet.hpp"
5
+ #include "containers/NamedBitSet.ipp"
6
+ #include "utils/hash.hpp"
7
+
8
+ namespace RuBLE {
9
+ // enum class PeripheralStatus : std::size_t {
10
+ // INITIALIZED = 0, CONNECTED, CONNECTABLE, PAIRED
11
+ // };
12
+ class Peripheral {
13
+ public:
14
+ static constexpr const std::string_view StatusFlagNames[] = {
15
+ "initialized", "connected", "connectable", "paired"
16
+ };
17
+ using StatusFlagSet = NamedBitSet<StatusFlagNames>;
18
+ // using Status = RuBLE::PeripheralStatus;
19
+
20
+ using DataObject [[maybe_unused]] = Data_Object<Peripheral>;
21
+ using Owner = Adapter;
22
+ using ServiceMap = std::map<BluetoothUUID, std::shared_ptr<Service>>;
23
+ using PartialResourceIdentifier = std::string_view;
24
+ static bool unpair_all_on_exit;
25
+ protected:
26
+ Owner *_owner;
27
+ std::shared_ptr<SimpleBLE::Peripheral> _peripheral;
28
+ SimpleBLE::BluetoothAddress _addr;
29
+ SimpleBLE::BluetoothAddressType _addr_type;
30
+ std::shared_ptr<Callback> _on_connected;
31
+ std::shared_ptr<Callback> _on_disconnected;
32
+ std::shared_ptr<ServiceRegistry> _service_registry;
33
+ StatusFlagSet _service_cache_tag;
34
+ VALUE _self = Qnil;
35
+ std::optional<PartialResourceIdentifier> _resource_identifier;
36
+
37
+ public:
38
+ // static std::shared_ptr<Peripheral> create(const SimpleBLE::Peripheral &peripheral);
39
+
40
+ [[nodiscard]] Object self() const;
41
+ Peripheral() = delete;
42
+ Peripheral(const SimpleBLE::Peripheral&, Owner*);
43
+ ~Peripheral() = default;
44
+
45
+ // Peripheral(const Peripheral&) = delete;
46
+ // Peripheral(Peripheral&&) = delete;
47
+ [[nodiscard]] bool initialized() const;
48
+
49
+ [[nodiscard]] const Owner *owner() const;
50
+ Owner *owner();
51
+
52
+ SimpleBLE::Peripheral &get();
53
+ [[nodiscard]] const SimpleBLE::Peripheral &get() const;
54
+
55
+ [[nodiscard]] std::string identifier() const;
56
+ [[nodiscard]] constexpr const BluetoothAddress &address() const;
57
+ [[nodiscard]] constexpr const BluetoothAddressType &address_type() const;
58
+
59
+ // constexpr std::string this_resource_identifier() const;
60
+ // constexpr ResourceUniqueIdentifier<Peripheral> full_resource_identifier() const;
61
+
62
+ [[nodiscard]] std::map<uint16_t, ByteArray> manufacturer_data() const;
63
+ [[nodiscard]] int16_t rssi() const;
64
+ [[nodiscard]] int16_t tx_power() const;
65
+ [[nodiscard]] uint16_t mtu() const;
66
+ [[nodiscard]] bool is_connected() const;
67
+ [[nodiscard]] bool is_connectable() const;
68
+ [[nodiscard]] bool is_paired() const;
69
+ [[nodiscard]] StatusFlagSet status_flags() const;
70
+ [[nodiscard]] std::vector<std::string_view> statuses() const;
71
+ void connect();
72
+ void disconnect();
73
+ void unpair();
74
+
75
+ [[nodiscard]] const std::map<BluetoothUUID, std::shared_ptr<Service>> &services() const;
76
+ std::shared_ptr<Service> operator[](const BluetoothUUID &svcUuid) const;
77
+
78
+ [[nodiscard]] ByteArray read(BluetoothUUID const &service, BluetoothUUID const &characteristic);
79
+ [[nodiscard]] ByteArray read(BluetoothUUID const &service, BluetoothUUID const &characteristic,
80
+ BluetoothUUID const &descriptor);
81
+ void write(BluetoothUUID const &service, BluetoothUUID const &characteristic,
82
+ BluetoothUUID const &descriptor, const ByteArray &data);
83
+ void write_request(BluetoothUUID const &service, BluetoothUUID const &characteristic,
84
+ const ByteArray &data);
85
+ void write_command(BluetoothUUID const &service, BluetoothUUID const &characteristic,
86
+ const ByteArray &data);
87
+ void notify(BluetoothUUID const &service, BluetoothUUID const &characteristic,
88
+ std::function<void(ByteArray payload)> callback);
89
+ void indicate(BluetoothUUID const &service, BluetoothUUID const &characteristic,
90
+ std::function<void(ByteArray payload)> callback);
91
+ void unsubscribe(BluetoothUUID const &service, BluetoothUUID const &characteristic);
92
+
93
+ [[nodiscard]] std::string to_s() const;
94
+ // bool operator==(const Peripheral &ap) const;
95
+
96
+ void on_connect(Object on_connect);
97
+ void on_disconnect(Object on_disconnect);
98
+ void ruby_mark() const;
99
+
100
+ template<typename, class, class> friend class Registry;
101
+ friend void Init_Registries();
102
+ };
103
+
104
+ constexpr const BluetoothAddress &Peripheral::address() const { return _addr; /*_peripheral->address();*/ }
105
+ constexpr const BluetoothAddressType &Peripheral::address_type() const {
106
+ return _addr_type;
107
+ }
108
+ }
@@ -0,0 +1,115 @@
1
+ #include "types/declarations.hpp"
2
+ #include "utils/exception_handling.hpp"
3
+
4
+ using namespace std::chrono_literals;
5
+
6
+ namespace RuBLE {
7
+ static_assert(!AdapterRegistry::is_owned);
8
+ static_assert(PeripheralRegistry::is_owned);
9
+ static_assert(ServiceRegistry::is_owned);
10
+ static_assert(CharacteristicRegistry::is_owned);
11
+
12
+ [[maybe_unused]] InRuby::Guard main_thread_in_ruby;
13
+
14
+
15
+ void Init_BluetoothAddressType() {
16
+ rb_cBluetoothAddressType = Utils::define_described_enum<BluetoothAddressType>("BluetoothAddressType", rb_mRuBLE);
17
+ }
18
+
19
+ void Init_DeInit() {
20
+ rb_mRuBLE
21
+ .define_singleton_function("shutdown", &DeInit); // to make it easier to break in gdb from ruby
22
+ rb_set_end_proc(DeInit, Qnil);
23
+ }
24
+
25
+ void DeInit(VALUE = Qnil) {
26
+ static std::atomic_flag called = false;
27
+ // if called is already true, this is a no-op
28
+ if (called.test_and_set()) return;
29
+
30
+
31
+ // todo: confirm this only applies to peripherals we ourselves have paired
32
+ if (adapterRegistry && Peripheral::unpair_all_on_exit) {
33
+ adapterRegistry->for_each([](std::shared_ptr<Adapter> &adapter) {
34
+ if (adapter->scan_is_active()) adapter->scan_stop();
35
+ adapter->_peripheral_registry->for_each([](auto &peripheral) {
36
+ if (peripheral->is_paired()) peripheral->unpair();
37
+ if (peripheral->is_connected()) {
38
+ std::cout << "Peripheral " << peripheral << " is connected. Disconnecting on shutdown." << std::endl;
39
+ peripheral->disconnect();
40
+ }
41
+ });
42
+ });
43
+ }
44
+
45
+ // Wait to ensure rubyQueue has stopped (if it hasn't yet)
46
+ RubyQueue::stop_on_exit(Qnil);
47
+ // rubyQueue->wait_for_stop();
48
+
49
+ // TODO(?): clear factories so ruby objects deallocate in this instance
50
+ // (we might not be segfaulting anymore, making this unneeded)
51
+ }
52
+
53
+ [[maybe_unused]] void c_debug() {
54
+ #ifdef RUBLE_DEBUG
55
+ std::cerr << "Insert gbb breakpoint here" << std::endl;
56
+ #endif
57
+ }
58
+
59
+ void Init_ExceptionHandling() {
60
+ ExceptionHandling::wrap_termination_handler();
61
+ // register_handler<Rice::Exception>(RuBLE::handle_exception<Rice::Exception>);
62
+ }
63
+
64
+ void Init_Utils() {
65
+ rb_mUtils = Rice::define_module_under(rb_mRuBLE, "Utils");
66
+
67
+ if constexpr (!DEBUG) return;
68
+ rb_mUtils.define_module_function("throw_cxx_exception", ExceptionHandling::throw_exception);
69
+ }
70
+
71
+ //static_assert(Identifiers::PartiallyIdentifiableResource<Adapter>);
72
+ //static_assert(std::same_as<RuBLE::Identifiers::IDTypes::Full<RuBLE::Adapter, true>::tuple, std::tuple<std::string>>);
73
+
74
+ // See https://jasonroelofs.com/rice/4.x
75
+ void Init_RuBLE() {
76
+ RuBLE::main_thread_in_ruby = RuBLE::in_ruby.assert_in_ruby_guard();
77
+ assert(RuBLE::main_thread_in_ruby);
78
+ Init_ExceptionHandling();
79
+
80
+ #ifdef HAVE_VALGRIND
81
+ VALGRIND_PRINTF_BACKTRACE("Enabling leak checking now.\n");
82
+ VALGRIND_CLO_CHANGE("--leak-check=full");
83
+ #endif
84
+ Rice::detail::Registries::instance.instances.isEnabled = true;
85
+ rb_mRuBLE = Rice::define_module("RuBLE");
86
+ rubyQueue = RubyQueue::instance();
87
+
88
+ #ifdef RUBLE_DEBUG
89
+ std::cout << "RubyQueue: " << Utils::to_hex_addr(rubyQueue.get()) << std::endl;
90
+ rb_mRuBLE.define_singleton_function("c_debug", &c_debug); // to make it easier to break in gdb from ruby
91
+ #endif
92
+ rb_mRuBLEUnderlying = Rice::define_module_under(rb_mRuBLE, "Underlying");
93
+ Init_Utils();
94
+ Init_BluetoothAddressType();
95
+ Init_ByteArray();
96
+ Init_Descriptor();
97
+ Init_Characteristic();
98
+ Init_Service();
99
+ Init_Peripheral();
100
+ Init_Adapter();
101
+ Init_Registries();
102
+ Init_DeInit();
103
+
104
+ rubyQueue->start();
105
+ rubyQueue->ensure_started();
106
+ }
107
+ }
108
+
109
+ extern "C" [[maybe_unused]] void Init_RuBLE() {
110
+ RuBLE::Init_RuBLE();
111
+ }
112
+
113
+ extern "C" [[maybe_unused]] void Init_ruBLE() {
114
+ RuBLE::Init_RuBLE();
115
+ }
@@ -0,0 +1,112 @@
1
+ #include "bindings/Service.hpp"
2
+
3
+ #include <utility>
4
+ #include "bindings/Characteristic.hpp"
5
+ #include "management/Registry.hpp"
6
+ #include "management/RegistryFactory.hpp"
7
+ #include "bindings/Peripheral.hpp"
8
+ #include "types/declarations.hpp"
9
+
10
+ namespace RuBLE {
11
+ Service::Service(const SimpleBLE::Service &service, Owner *owner) :
12
+ _owner(owner),
13
+ _service(std::make_shared<SimpleBLE::Service>(service)),
14
+ _uuid(_service->uuid()),
15
+ _characteristic_registry(characteristicRegistryFactory->registry(this)),
16
+ _self(DataObject(*this)) {}
17
+
18
+ Object Service::self() const {
19
+ return _self;
20
+ }
21
+
22
+ SimpleBLE::Service &Service::get() { return *_service; }
23
+
24
+ const SimpleBLE::Service &Service::get() const { return *_service; }
25
+
26
+ ByteArray Service::data() const { return _service->data(); }
27
+
28
+ const std::map<BluetoothUUID, std::shared_ptr<Characteristic>> &Service::characteristics() const {
29
+ if (!rb_during_gc()) {
30
+ // we can't create new characteristics during GC
31
+ for (const auto &c : _service->characteristics()) _characteristic_registry->fetch(c);
32
+ }
33
+ return _characteristic_registry->data();
34
+ // std::vector<SimpleBLE::Characteristic> unwrappedCharacteristics = _service->characteristics();
35
+ // auto objects = _characteristic_registry->map_to_objects(unwrappedCharacteristics);
36
+ //
37
+ // std::function<BluetoothUUID(const Characteristic&)> mapFn = &Characteristic::uuid;
38
+ // auto result = map_by<BluetoothUUID, std::shared_ptr<Characteristic>>(objects, mapFn);
39
+ // return std::move(result);
40
+ }
41
+
42
+ std::shared_ptr<Characteristic> Service::operator[](const BluetoothUUID &charUuid) const {
43
+ // only check SimpleBLE backend for characteristic if not already instantiated
44
+ if (!_characteristic_registry->contains(charUuid)) const auto _result = characteristics();
45
+ return _characteristic_registry->at(charUuid);
46
+ }
47
+
48
+
49
+ ByteArray Service::read(const BluetoothUUID &characteristic) {
50
+ return peripheral()->read(uuid(), characteristic);
51
+ }
52
+
53
+ ByteArray
54
+ Service::read(const BluetoothUUID &characteristic, const BluetoothUUID &descriptor) {
55
+ return peripheral()->read(uuid(), characteristic, descriptor);
56
+ }
57
+
58
+ void
59
+ Service::write(const BluetoothUUID &characteristic, const BluetoothUUID &descriptor,
60
+ const ByteArray &data) {
61
+ peripheral()->write(uuid(), characteristic, descriptor, data);
62
+ }
63
+
64
+ void
65
+ Service::write_request(const BluetoothUUID &characteristic, const ByteArray &data) {
66
+ peripheral()->write_request(uuid(), characteristic, data);
67
+ }
68
+
69
+ void
70
+ Service::write_command(const BluetoothUUID &characteristic, const ByteArray &data) {
71
+ peripheral()->write_command(uuid(), characteristic, data);
72
+ }
73
+
74
+ void Service::notify(const BluetoothUUID &characteristic,
75
+ std::function<void(ByteArray)> callback) {
76
+ peripheral()->notify(uuid(), characteristic, std::move(callback));
77
+ }
78
+
79
+ void Service::indicate(const BluetoothUUID &characteristic,
80
+ std::function<void(ByteArray)> callback) {
81
+ peripheral()->indicate(uuid(), characteristic, std::move(callback));
82
+ }
83
+
84
+ void Service::unsubscribe(const BluetoothUUID &characteristic) {
85
+ peripheral()->unsubscribe(uuid(), characteristic);
86
+ }
87
+
88
+ std::string Service::to_s() const {
89
+ std::ostringstream oss;
90
+ oss << Utils::basic_object_inspect_start(*this) << " ";
91
+ oss << "@uuid=\"" << uuid() << "\" ";
92
+ oss << "@data=\"" << data() << "\" ";
93
+ // oss << "#characteristics=" << characteristics().size();
94
+ oss << ">";
95
+ return oss.str();
96
+ }
97
+
98
+ void Service::ruby_mark() const {
99
+ rb_gc_mark(self());
100
+ for (const auto &[uuid, characteristic]: characteristics())
101
+ Rice::ruby_mark(characteristic.get());
102
+ }
103
+
104
+ void Init_Service() {
105
+ rb_cService = define_class_under<Service>(rb_mRuBLE, "Service")
106
+ .define_method("uuid", &Service::uuid) // returns BluetoothUUID
107
+ .define_method("data", &Service::data) // returns SimpleBLE::ByteArray
108
+ .define_method("characteristics", &Service::characteristics) // returns std::vector<Characteristic>
109
+ .define_method("inspect", &Service::to_s);
110
+ define_class_under<std::shared_ptr<RuBLE::Service>>(rb_mRuBLE, "ServicePtr");
111
+ }
112
+ }
@@ -0,0 +1,61 @@
1
+ #pragma once
2
+
3
+ #include "types/declarations.hpp"
4
+ #include "types/ruby.hpp"
5
+ #include "utils/hash.hpp"
6
+
7
+ namespace RuBLE {
8
+ class Peripheral;
9
+ // template<Identifiers::OwnerTypedResource T>
10
+ // class ResourceUniqueIdentifier;
11
+
12
+ class Service {
13
+ public:
14
+ using Owner = Peripheral;
15
+ using DataObject = Data_Object<Service>;
16
+ protected:
17
+ Owner *_owner;
18
+ std::shared_ptr<SimpleBLE::Service> _service;
19
+ std::shared_ptr<CharacteristicRegistry> _characteristic_registry;
20
+ BluetoothUUID _uuid;
21
+ VALUE _self = Qnil;
22
+
23
+ public:
24
+ [[nodiscard]] Object self() const;
25
+ Service() = delete;
26
+ Service(const SimpleBLE::Service&, Owner*);
27
+ SimpleBLE::Service &get();
28
+ [[nodiscard]] const SimpleBLE::Service &get() const;
29
+ [[nodiscard]] constexpr const Owner *owner() const { return _owner; }
30
+ constexpr Owner *owner() { return _owner; }
31
+ [[nodiscard]] constexpr const Peripheral *peripheral() const { return owner(); }
32
+ constexpr Peripheral *peripheral() { return owner(); }
33
+
34
+ [[nodiscard]] constexpr const BluetoothUUID &uuid() const { return _uuid; }
35
+ // [[nodiscard]] constexpr ResourceUniqueIdentifier<Service> full_resource_identifier() const;
36
+ // [[nodiscard]] constexpr BluetoothUUID this_resource_identifier() const { return uuid(); }
37
+
38
+
39
+ [[nodiscard]] ByteArray data() const;
40
+ [[nodiscard]] const std::map<BluetoothUUID, std::shared_ptr<Characteristic>> &characteristics() const;
41
+ [[nodiscard]] std::shared_ptr<Characteristic> operator[](const BluetoothUUID &charUuid) const;
42
+
43
+ [[nodiscard]] ByteArray read(const BluetoothUUID &characteristic);
44
+ [[nodiscard]] ByteArray read(const BluetoothUUID &characteristic, const BluetoothUUID &descriptor);
45
+ void write(const BluetoothUUID &characteristic, const BluetoothUUID &descriptor, const ByteArray &data);
46
+ void write_request(const BluetoothUUID &characteristic, const ByteArray &data);
47
+ void write_command(const BluetoothUUID &characteristic, const ByteArray &data);
48
+ void notify(const BluetoothUUID &characteristic, std::function<void(ByteArray payload)> callback);
49
+ void indicate(const BluetoothUUID &characteristic, std::function<void(ByteArray payload)> callback);
50
+ void unsubscribe(const BluetoothUUID &characteristic);
51
+ void ruby_mark() const;
52
+
53
+ [[nodiscard]] std::string to_s() const;
54
+ // bool operator==(const Service &ap) const;
55
+ // bool operator==(const Service &ap) const;
56
+
57
+ friend void Init_Service();
58
+ friend void Init_Registries();
59
+ template<typename, class, class> friend class Registry;
60
+ };
61
+ }
@@ -0,0 +1,48 @@
1
+ #pragma once
2
+
3
+ #include "types/ruby.hpp"
4
+ #include "types/declarations.hpp"
5
+
6
+ namespace RuBLE {
7
+ extern std::shared_ptr<RubyQueue> rubyQueue;
8
+
9
+ extern Rice::Module rb_mRuBLE;
10
+ extern Rice::Module rb_mRuBLEUnderlying;
11
+ extern Rice::Module rb_mUtils;
12
+ [[maybe_unused]] extern BluetoothAddressType_DT rb_cBluetoothAddressType;
13
+ extern Characteristic_DT rb_cCharacteristic;
14
+ [[maybe_unused]] extern CharacteristicCapabilityType_DT rb_cCharacteristicCapabilityType;
15
+ [[maybe_unused]] extern Descriptor_DT rb_cDescriptor;
16
+ extern Service_DT rb_cService;
17
+ [[maybe_unused]] extern ServiceRegistry_DT rb_cServiceRegistry;
18
+ extern AdapterRegistry_DT rb_cAdapterRegistry;
19
+ [[maybe_unused]] extern PeripheralRegistry_DT rb_cPeripheralRegistry;
20
+ [[maybe_unused]] extern CharacteristicRegistry_DT rb_cCharacteristicRegistry;
21
+ extern ByteArray_DT rb_cByteArray;
22
+ extern Peripheral_DT rb_cPeripheral;
23
+ extern Adapter_DT rb_cAdapter;
24
+
25
+
26
+ extern std::shared_ptr<AdapterRegistryFactory> adapterRegistryFactory;
27
+ extern std::shared_ptr<AdapterRegistry> adapterRegistry;
28
+ extern std::shared_ptr<PeripheralRegistryFactory> peripheralRegistryFactory;
29
+ extern std::shared_ptr<ServiceRegistryFactory> serviceRegistryFactory;
30
+ extern std::shared_ptr<CharacteristicRegistryFactory> characteristicRegistryFactory;
31
+
32
+ extern void Init_RuBLE();
33
+ extern void Init_Adapter();
34
+ extern void Init_BluetoothAddressType();
35
+ extern void Init_Characteristic();
36
+ extern void Init_ByteArray();
37
+ extern void Init_Descriptor();
38
+ extern void Init_Peripheral();
39
+ extern void Init_Registries();
40
+ extern void Init_Service();
41
+ extern void Init_Utils();
42
+ extern void Init_DeInit();
43
+ extern void DeInit(VALUE);
44
+ extern void c_debug();
45
+ }
46
+
47
+ extern "C" [[maybe_unused]] void Init_RuBLE();
48
+ extern "C" [[maybe_unused]] void Init_ruBLE();
@@ -0,0 +1,43 @@
1
+ #include "types/declarations.hpp"
2
+ #include "types/ruby.hpp"
3
+ #include "types/SimpleBLE.hpp"
4
+ #include "common.hpp"
5
+ #include "utils/ruby_context.hpp"
6
+ #include "utils/hash.hpp"
7
+ #include "utils/garbage_collection.hpp"
8
+ #include "utils/exception_handling.hpp"
9
+ #include "bindings/Characteristic.hpp"
10
+ #include "management/Registry.hpp"
11
+ #include "bindings/Peripheral.hpp"
12
+ #include "bindings/Adapter.hpp"
13
+ #include "bindings/Characteristic.hpp"
14
+ #include <memory>
15
+
16
+ namespace RuBLE {
17
+ thread_local InRuby in_ruby {};
18
+
19
+ std::shared_ptr<RubyQueue> rubyQueue;
20
+ Rice::Module rb_mRuBLE;
21
+ Rice::Module rb_mRuBLEUnderlying;
22
+ Rice::Module rb_mUtils;
23
+ [[maybe_unused]] BluetoothAddressType_DT rb_cBluetoothAddressType;
24
+ Characteristic_DT rb_cCharacteristic;
25
+ [[maybe_unused]] CharacteristicCapabilityType_DT rb_cCharacteristicCapabilityType;
26
+ [[maybe_unused]] Descriptor_DT rb_cDescriptor;
27
+ Service_DT rb_cService;
28
+ [[maybe_unused]] ServiceRegistry_DT rb_cServiceRegistry;
29
+ AdapterRegistry_DT rb_cAdapterRegistry;
30
+ [[maybe_unused]] PeripheralRegistry_DT rb_cPeripheralRegistry;
31
+ [[maybe_unused]] CharacteristicRegistry_DT rb_cCharacteristicRegistry;
32
+ ByteArray_DT rb_cByteArray;
33
+ Peripheral_DT rb_cPeripheral;
34
+ Adapter_DT rb_cAdapter;
35
+
36
+ std::shared_ptr<AdapterRegistryFactory> adapterRegistryFactory;
37
+ std::shared_ptr<AdapterRegistry> adapterRegistry;
38
+ std::shared_ptr<PeripheralRegistryFactory> peripheralRegistryFactory;
39
+ std::shared_ptr<ServiceRegistryFactory> serviceRegistryFactory;
40
+ std::shared_ptr<CharacteristicRegistryFactory> characteristicRegistryFactory;
41
+
42
+ bool Peripheral::unpair_all_on_exit = true;
43
+ }
@@ -0,0 +1,62 @@
1
+ # Included from ./Makefile
2
+
3
+
4
+ # TODO: add compile rules for `all` and `install` to
5
+ # create build dir and invoke cmake with appropriate arguments
6
+
7
+
8
+ # install: install-so install-rb
9
+ # install-so: Makefile
10
+ # install-rb: pre-install-rb do-install-rb install-rb-default
11
+ # install-rb-default: pre-install-rb-default do-install-rb-default
12
+ # all: Makefile
13
+ # static: $(STATIC_LIB)
14
+ # .PHONY: all install static install-so install-rb
15
+ # .PHONY: clean clean-so clean-static clean-rb
16
+
17
+ pre-build: # TODO: add deps
18
+ echo mkdir -p $(build_dir)
19
+ echo cd $(build_dir)
20
+ echo cmake $(ext_dir) -A $(plattorm) -G 'Unix Makefile' -C build-config.cmake --install-prefix ...
21
+
22
+
23
+ $(DLLIB): pre-build
24
+ # should install to $(TARGET_SO_DIR)
25
+ echo make -C $(build_dir) $@
26
+
27
+
28
+ # TODO add static target in cmake
29
+ $(STATIC_LIB): pre-build
30
+ echo make -C $(build_dir) $@
31
+ echo "ERROR: Static builds not supported"
32
+ exit 2
33
+
34
+ so: $(DLLIB) # todo: is this already defined?
35
+
36
+ all: so static
37
+
38
+ install-static: static
39
+ # todo: why don't we not use cmake at all for the install?
40
+ echo make -C $(build_dir) $@
41
+
42
+ install-so-local: so
43
+ # copy so into lib/ruBLE/ruBLE.so
44
+
45
+ install-so: so
46
+ # todo: why don't we not use cmake at all for the install?
47
+ echo make -C $(build_dir) $@
48
+
49
+ clean-static:
50
+ echo make -C $(build_dir) clean
51
+
52
+ clean-so:
53
+ echo make -C $(build_dir) clean
54
+
55
+ distclean-so:
56
+ rm -Rf $(build_dir)
57
+
58
+ distclean-static:
59
+ rm -Rf $(build_dir)
60
+
61
+
62
+
@@ -0,0 +1,18 @@
1
+ #include "CharacteristicValueTracker.hpp"
2
+
3
+ namespace RuBLE {
4
+ bool CharacteristicValueTracker::record_new_value(ByteArray new_data) const {
5
+ if (!value_tracking()) return true; // always assume value changed if no tracking
6
+ std::shared_ptr<ByteArray> old_value =
7
+ std::exchange(_value, std::make_shared<ByteArray>(std::move(new_data)));
8
+
9
+ // if no previous value, returns whether or not new value is empty
10
+ if (!old_value) return _value->length() > 0;
11
+ // otherwise, return true if old and new values differ
12
+ return *old_value != *_value;
13
+ }
14
+
15
+ void CharacteristicValueTracker::ruby_mark() const {
16
+ if (_value) _value->ruby_mark();
17
+ }
18
+ }
@@ -0,0 +1,40 @@
1
+ #pragma once
2
+ #include "types/declarations.hpp"
3
+ #include <optional>
4
+
5
+ namespace RuBLE {
6
+ // just felt cleaner to abstract out this logic. open to being persuaded otherwise
7
+ class CharacteristicValueTracker {
8
+ protected:
9
+ std::optional<bool> _track_values;
10
+ mutable std::shared_ptr<ByteArray> _value;
11
+
12
+ constexpr CharacteristicValueTracker(std::optional<bool> enabled = std::nullopt) :
13
+ _track_values(enabled) {}
14
+
15
+ // returns true if value changed
16
+ bool record_new_value(ByteArray new_data) const;
17
+
18
+ public:
19
+ virtual bool can_notify() const = 0;
20
+ constexpr void set_value_tracking(bool new_value) {
21
+ if (new_value && !can_notify()) {
22
+ throw std::runtime_error("Characteristic does not have 'notify' capability.");
23
+ } else if (!new_value) {
24
+ _value = nullptr;
25
+ }
26
+ _track_values = new_value;
27
+ }
28
+
29
+ constexpr bool value_tracking() const {
30
+ return _track_values.value_or(can_notify());
31
+ }
32
+
33
+ [[nodiscard]] constexpr const std::shared_ptr<ByteArray> &last_value() const {
34
+ return _value;
35
+ }
36
+
37
+ virtual void ruby_mark() const;
38
+ };
39
+ } // RuBLE
40
+
@@ -0,0 +1,4 @@
1
+ #include "Rubyable.hpp"
2
+
3
+ namespace RuBLE {
4
+ } // RuBLE
@@ -0,0 +1,46 @@
1
+ #pragma once
2
+ #include <iostream>
3
+ #include <concepts>
4
+ #include "types/ruby.hpp"
5
+ #include "utils/human_type_names.hpp"
6
+ #include "utils/ruby_context.hpp"
7
+ #include "utils/hexadecimal.hpp"
8
+
9
+ namespace RuBLE {
10
+ // TODO: Use this for Adapter/Peripheral/Service/Characteristic/Descriptor
11
+ template <class T>
12
+ class Rubyable {
13
+ protected:
14
+ VALUE _self = Qnil;
15
+
16
+ virtual constexpr VALUE &get_or_allocate_rb_object() {
17
+ if (_self != Qnil) return _self;
18
+ if (in_ruby) {
19
+ _self = Rice::Data_Object<T>(*dynamic_cast<T*>(this));
20
+ std::cerr << "Note: allocated ruby object for ";
21
+ std::cerr << Utils::human_type_name(*this) << " (";
22
+ std::cerr << Utils::ptr_to_hex_addr(this) << ") with VALUE ";
23
+ std::cerr << _self << "." << std::endl;
24
+ } else {
25
+ std::cerr << "Warning: could not allocate ruby object for ";
26
+ std::cerr << Utils::human_type_name(*this) << " (";
27
+ std::cerr << Utils::ptr_to_hex_addr(this) << ") because ";
28
+ std::cerr << "inRuby is false." << std::endl;
29
+ }
30
+ return _self;
31
+ }
32
+ public:
33
+ constexpr Rubyable() = default;
34
+
35
+ template<class U = Rice::Data_Object<T>> requires std::is_base_of_v<Rice::Object, U>
36
+ U self() const {
37
+ if (_self == Qnil) _self = get_or_allocate_rb_object();
38
+ return { _self };
39
+ }
40
+
41
+ virtual void ruby_mark() const {
42
+ if (_self != Qnil) rb_gc_mark(_self);
43
+ }
44
+ };
45
+ } // RuBLE
46
+