ruble 0.0.3.alpha

Sign up to get free protection for your applications and to get access to all the features.
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
+