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,171 @@
1
+ #include "types/declarations.hpp"
2
+ #include "bindings/Peripheral.hpp"
3
+ #include "bindings/Service.hpp"
4
+ #include "bindings/Characteristic.hpp"
5
+ #include "bindings/Descriptor.hpp"
6
+
7
+ #include <utility>
8
+ #include "management/RubyQueue.hpp"
9
+ #include "containers/Callback.hpp"
10
+ #include "utils/inspection.hpp"
11
+
12
+ namespace RuBLE {
13
+ Characteristic::Characteristic(const SimpleBLE::Characteristic &characteristic,
14
+ Owner *owner) :
15
+ _owner(owner),
16
+ _characteristic(std::make_shared<SimpleBLE::Characteristic>(characteristic)),
17
+ _uuid(_characteristic->uuid()),
18
+ _self(DataObject(*this)) {
19
+
20
+ if (capabilities()["notify"]) {
21
+ service()->notify(uuid(), [this](const ByteArray& data) {
22
+ RubyQueue::FnType fn = std::bind(&Characteristic::fire_on_notify, std::cref(*this), data); // NOLINT(*-avoid-bind)
23
+ rubyQueue->push(fn);
24
+ });
25
+ }
26
+
27
+ if (capabilities()["indicate"]) {
28
+ service()->indicate(uuid(), [this](const ByteArray &data) {
29
+ RubyQueue::FnType fn = std::bind(&Characteristic::fire_on_indicate, std::cref(*this), data); // NOLINT(*-avoid-bind)
30
+ rubyQueue->push(fn);
31
+ });
32
+ }
33
+ }
34
+
35
+ Object Characteristic::self() const {
36
+ return _self;
37
+ }
38
+
39
+ SimpleBLE::Characteristic &Characteristic::get() { return *_characteristic; }
40
+ const SimpleBLE::Characteristic &Characteristic::get() const { return *_characteristic; }
41
+
42
+ const std::map<BluetoothUUID, std::shared_ptr<Descriptor>> &Characteristic::descriptors() const {
43
+ if (_descriptors) return *_descriptors;
44
+ auto result = Descriptor::make_map(_characteristic->descriptors(), const_cast<Characteristic*>(this));
45
+ _descriptors = std::move(result);
46
+ return *_descriptors;
47
+ }
48
+
49
+ std::shared_ptr<Descriptor> Characteristic::operator[](const BluetoothUUID &descUuid) const {
50
+ // only check SimpleBLE backend for descriptor if not already instantiated
51
+ if (!_descriptors || !_descriptors->contains(descUuid)) const auto _result = descriptors();
52
+ return _descriptors->at(descUuid);
53
+ // TODO(?): catch (std::out_of_range &ex)
54
+ }
55
+
56
+ std::vector<std::string> Characteristic::capability_names() const { return capabilities().flag_name_strs(); }
57
+
58
+ ByteArray Characteristic::read(bool force) {
59
+ // if tracking is enabled, assume we'll get the value via callback
60
+ if (!force && value_tracking() && last_value()) return *last_value();
61
+
62
+ ByteArray value = service()->read(uuid());
63
+ record_new_value(value);
64
+ return std::move(value);
65
+ }
66
+
67
+ ByteArray Characteristic::read(const BluetoothUUID &descriptor) {
68
+ return service()->read(uuid(), descriptor);
69
+ }
70
+ void Characteristic::write(const BluetoothUUID &descriptor, const ByteArray &data) {
71
+ service()->write(uuid(), descriptor, data);
72
+ }
73
+ void Characteristic::write_request(const ByteArray &data) {
74
+ service()->write_request(uuid(), data);
75
+ }
76
+ void Characteristic::write_command(const ByteArray &data) {
77
+ service()->write_command(uuid(), data);
78
+ }
79
+
80
+ void Characteristic::set_on_notify(Object cb) {
81
+ if (!_on_notify) _on_notify = std::make_shared<Callback>(2);
82
+ _on_notify->set(std::move(cb));
83
+ }
84
+
85
+ void Characteristic::set_on_indicate(Object cb) {
86
+ if (!_on_indicate) _on_indicate = std::make_shared<Callback>(2);
87
+ _on_indicate->set(std::move(cb));
88
+ }
89
+
90
+ void Characteristic::fire_on_notify(const ByteArray &data) const {
91
+ if (DEBUG) std::cout << "Characteristic::fire_on_notify called with " << data.inspect() << std::endl;
92
+ if (!record_new_value(data)) return; // if value tracking is enabled, skip callback unless value changed
93
+ if (_on_notify) _on_notify->fire(Rice::detail::To_Ruby<SimpleBLE::ByteArray>().convert(data), self());
94
+ }
95
+
96
+ void Characteristic::fire_on_indicate(const ByteArray &data) const {
97
+ if (DEBUG) std::cout << "Characteristic::fire_on_indicate called with " << data.inspect() << std::endl;
98
+ if (_on_indicate) _on_indicate->fire(Rice::detail::To_Ruby<SimpleBLE::ByteArray>().convert(data), self());
99
+ }
100
+
101
+ void Characteristic::unsubscribe() {
102
+ service()->unsubscribe(uuid());
103
+ }
104
+
105
+ std::string Characteristic::to_s() const {
106
+ std::ostringstream oss;
107
+ oss << Utils::basic_object_inspect_start(*this) << " ";
108
+ oss << "@uuid=" << uuid() << " ";
109
+ oss << "@capabilities=" << capabilities();
110
+ // oss << "#characteristics=" << characteristics().size();
111
+ oss << ">";
112
+ return oss.str();
113
+ }
114
+
115
+ Rice::String Characteristic::inspect() const {
116
+ return to_s();
117
+ }
118
+
119
+
120
+ void Characteristic::ruby_mark() const {
121
+ rb_gc_mark(self());
122
+ CharacteristicValueTracker::ruby_mark();
123
+ if (_on_indicate) Rice::ruby_mark(_on_indicate.get());
124
+ if (_on_notify) Rice::ruby_mark(_on_notify.get());
125
+ if (_descriptors) {
126
+ for (const auto &[uuid, descriptor]: *_descriptors) Rice::ruby_mark(descriptor.get());
127
+ }
128
+ }
129
+
130
+ void Init_Characteristic() {
131
+ // FIXME: A bunch of these classes inherit from abstract base classes (e.g. BaseAdapter),
132
+ // and are also subclassed in different implementations (I think).
133
+ // Do we need the constructor and methods to return/receive pointers to work well
134
+ // (so the right virtual functions are called)? Not sure if rice is handling that all,
135
+ // or just not seeing any obvious bugs yet because the base classes are so minimal.
136
+ rb_cCharacteristic = define_class_under<Characteristic>(rb_mRuBLE, "Characteristic");
137
+
138
+ using CapabilityType = Characteristic::Capability;
139
+ rb_cCharacteristicCapabilityType = Utils::define_described_enum<CapabilityType>("CapabilityType", rb_cCharacteristic);
140
+
141
+ using CababilityFlags [[maybe_unused]] = Characteristic::CapabilityFlags;
142
+ // auto rb_cCharacteristicCapabilityFlags = define_class_under<CapabilityFlags>(rb_cCharacteristic, "CapabilityFlags")
143
+ // .define_method("flag_names", &CapabilityFlags::flag_names)
144
+ // ;
145
+
146
+ rb_cCharacteristic
147
+ .define_method("uuid", &Characteristic::uuid) // returns BluetoothUUID
148
+ .define_method("capabilities", &Characteristic::capability_names)
149
+ .define_method("can_read?", &Characteristic::can_read)
150
+ .define_method("can_write_request?", &Characteristic::can_write_request)
151
+ .define_method("can_write_command?", &Characteristic::can_write_command)
152
+ .define_method("can_notify?", &Characteristic::can_notify)
153
+ .define_method("can_indicate?", &Characteristic::can_indicate)
154
+ .define_method<Characteristic::ExposedReadFn>("read", &Characteristic::read, Arg("force") = false)
155
+ .define_method("write", &Characteristic::write)
156
+ .define_method("write_request", &Characteristic::write_request)
157
+ .define_method("write_command", &Characteristic::write_command)
158
+ .define_method("set_on_notify", &Characteristic::set_on_notify)
159
+ .define_method("set_on_indicate", &Characteristic::set_on_indicate)
160
+ // .define_method("unsubscribe", &Characteristic::unsubscribe)
161
+ .define_method("to_s", &Characteristic::to_s)
162
+ .define_method("inspect", &Characteristic::inspect)
163
+ .define_method("value_tracking", &Characteristic::value_tracking)
164
+ .define_method("value_tracking?", &Characteristic::value_tracking)
165
+ .define_method("value_tracking=", &Characteristic::set_value_tracking)
166
+ .define_method("last_value", &Characteristic::last_value)
167
+ ;
168
+ define_class_under<std::shared_ptr<RuBLE::Characteristic>>(rb_mRuBLE, "CharacteristicPtr");
169
+
170
+ }
171
+ }
@@ -0,0 +1,132 @@
1
+ #pragma once
2
+
3
+ #include "management/RubyQueue.hpp"
4
+ #include "containers/NamedBitSet.hpp"
5
+ #include "bindings/Descriptor.hpp"
6
+ #include "bindings/Service.hpp"
7
+ #include "containers/ByteArray.hpp"
8
+ #include "utils/hash.hpp"
9
+ #include "utils/inspection.hpp"
10
+ #include "concerns/CharacteristicValueTracker.hpp"
11
+
12
+ namespace RuBLE {
13
+
14
+ BOOST_DEFINE_FIXED_ENUM_CLASS(
15
+ CharacteristicCapabilityType,
16
+ std::size_t,
17
+ READ,
18
+ WRITE_REQUEST,
19
+ WRITE_COMMAND,
20
+ NOTIFY,
21
+ INDICATE
22
+ )
23
+ class Service;
24
+ class Characteristic : public CharacteristicValueTracker {
25
+ public:
26
+ static constexpr std::string_view CAPABILITY_NAMES[] = {
27
+ "read",
28
+ "write_request",
29
+ "write_command",
30
+ "notify",
31
+ "indicate"
32
+ };
33
+ using Capability = CharacteristicCapabilityType;
34
+ using CapabilityFlags = RuBLE::NamedBitSet<CAPABILITY_NAMES>;
35
+ using DataObject = Data_Object<Characteristic>;
36
+ using Owner = Service;
37
+ using CallbackFnType [[maybe_unused]] = std::function<void(ByteArray payload)>;
38
+ using DescriptorMap = std::map<BluetoothUUID, std::shared_ptr<Descriptor>>;
39
+ protected:
40
+ Owner *_owner;
41
+ std::shared_ptr<SimpleBLE::Characteristic> _characteristic;
42
+ BluetoothUUID _uuid;
43
+ std::shared_ptr<Callback> _on_notify;
44
+ std::shared_ptr<Callback> _on_indicate;
45
+ mutable std::optional<CapabilityFlags> _capabilities;
46
+ // NB: this is the only class where we're not using a Registry<>
47
+ // This can be a case study in if we can remove a ton of the registry stuff
48
+ // now that I figured out garbage collection
49
+ mutable std::optional<DescriptorMap> _descriptors;
50
+
51
+ VALUE _self = Qnil;
52
+
53
+ void fire_on_notify(const ByteArray &data) const;
54
+ void fire_on_indicate(const ByteArray &data) const;
55
+ public:
56
+ [[nodiscard]] Object self() const;
57
+ Characteristic() = delete;
58
+ Characteristic(const SimpleBLE::Characteristic&, Owner*);
59
+ SimpleBLE::Characteristic &get();
60
+ [[nodiscard]] const SimpleBLE::Characteristic &get() const;
61
+ [[nodiscard]] constexpr const Owner *owner() const;
62
+ constexpr Owner *owner();
63
+ [[nodiscard]] constexpr const Service *service() const;
64
+ constexpr Service *service();
65
+
66
+ [[nodiscard]] constexpr const BluetoothUUID &uuid() const;
67
+ // constexpr ResourceUniqueIdentifier<Characteristic> full_resource_identifier() const;
68
+ // constexpr BluetoothUUID this_resource_identifier() const;
69
+
70
+ [[nodiscard]] const std::map<BluetoothUUID, std::shared_ptr<Descriptor>> &descriptors() const;
71
+ [[nodiscard]] std::shared_ptr<Descriptor> operator[](const BluetoothUUID &descUuid) const;
72
+ [[nodiscard]] constexpr const CapabilityFlags &capabilities() const;
73
+
74
+ [[nodiscard]] std::vector<std::string> capability_names() const;
75
+ [[nodiscard]] constexpr bool can_read() const;
76
+ [[nodiscard]] constexpr bool can_write_request() const;
77
+ [[nodiscard]] constexpr bool can_write_command() const;
78
+ [[nodiscard]] constexpr bool can_notify() const override;
79
+ [[nodiscard]] constexpr bool can_indicate() const;
80
+
81
+ [[nodiscard]] ByteArray read(bool force = false);
82
+ [[nodiscard]] ByteArray read(const BluetoothUUID &descriptor);
83
+ void write(const BluetoothUUID &descriptor, const ByteArray &data);
84
+ void write_request(const ByteArray &data);
85
+ void write_command(const ByteArray &data);
86
+
87
+ void set_on_notify(Object cb);
88
+ void set_on_indicate(Object cb);
89
+ void unsubscribe();
90
+
91
+ [[nodiscard]] std::string to_s() const;
92
+ [[nodiscard]] Rice::String inspect() const;
93
+ // bool operator==(const Characteristic &ap) const;
94
+ // bool operator==(const Characteristic &ap) const;
95
+ void ruby_mark() const override;
96
+
97
+ friend void Init_Characteristic();
98
+ friend void Init_Registries();
99
+ template<typename, class, class> friend class Registry;
100
+ using ExposedReadFn = ByteArray(Characteristic::*)(bool);
101
+ };
102
+
103
+ constexpr const Characteristic::Owner *Characteristic::owner() const { return _owner; }
104
+
105
+ constexpr Characteristic::Owner *Characteristic::owner() { return _owner; }
106
+
107
+ constexpr const Service *Characteristic::service() const { return owner(); }
108
+
109
+ constexpr Service *Characteristic::service() { return owner(); }
110
+
111
+ constexpr const BluetoothUUID &Characteristic::uuid() const { return _uuid; }
112
+
113
+
114
+ // constexpr BluetoothUUID Characteristic::this_resource_identifier() const { return _uuid; }
115
+
116
+ constexpr const Characteristic::CapabilityFlags &Characteristic::capabilities() const {
117
+ if (_capabilities) return *_capabilities;
118
+ _capabilities = std::make_optional<CapabilityFlags>(_characteristic->capabilities());
119
+ return *_capabilities;
120
+ }
121
+
122
+ constexpr bool Characteristic::can_read() const { return capabilities()["read"]; }
123
+
124
+ constexpr bool Characteristic::can_write_request() const { return capabilities()["write_request"]; }
125
+
126
+ constexpr bool Characteristic::can_write_command() const { return capabilities()["write_command"]; }
127
+
128
+ constexpr bool Characteristic::can_notify() const { return capabilities()["notify"]; }
129
+
130
+ constexpr bool Characteristic::can_indicate() const { return capabilities()["indicate"]; }
131
+ }
132
+
@@ -0,0 +1,34 @@
1
+ #include "bindings/Descriptor.hpp"
2
+ #include "bindings/Characteristic.hpp"
3
+
4
+ namespace RuBLE {
5
+ Descriptor::Descriptor(BluetoothUUID uuid, Descriptor::Owner *owner) :
6
+ _owner(owner), _uuid(std::move(uuid)), _self(DataObject(*this)) {}
7
+
8
+ Descriptor::Descriptor(const SimpleBLE::Descriptor &descriptor, Descriptor::Owner *owner) :
9
+ Descriptor(const_cast<SimpleBLE::Descriptor&>(descriptor).uuid(), owner) {}
10
+
11
+ Object Descriptor::self() const { return _self; }
12
+
13
+ [[nodiscard]] ByteArray Descriptor::read() { return characteristic()->read(uuid()); }
14
+
15
+ void Descriptor::write(const ByteArray &data) { characteristic()->write(uuid(), data); }
16
+
17
+
18
+ Rice::String Descriptor::inspect() const {
19
+ return to_s();
20
+ }
21
+
22
+
23
+
24
+ void Init_Descriptor() {
25
+ rb_cDescriptor = define_class_under<Descriptor>(rb_mRuBLE, "Descriptor")
26
+ .define_method("uuid", &Descriptor::uuid) // returns BluetoothUUID
27
+ .define_method("inspect", &Descriptor::inspect)
28
+ .define_method("read", &Descriptor::read)
29
+ .define_method("write", &Descriptor::write)
30
+ ;
31
+ }
32
+
33
+ }
34
+
@@ -0,0 +1,69 @@
1
+ #pragma once
2
+
3
+ #include "types/declarations.hpp"
4
+ #include <utility>
5
+ #include <compare>
6
+ #include "utils/inspection.hpp"
7
+
8
+ namespace RuBLE {
9
+ class Characteristic;
10
+ class Descriptor {
11
+ public:
12
+ using DataObject = Descriptor_DO;
13
+ using Owner = Characteristic;
14
+ protected:
15
+ BluetoothUUID _uuid;
16
+ Owner *_owner = nullptr;
17
+ VALUE _self = Qnil;
18
+ public:
19
+ static std::map<BluetoothUUID, std::shared_ptr<Descriptor>> make_map(const auto &range, Descriptor::Owner *owner) {
20
+ std::map<BluetoothUUID, std::shared_ptr<Descriptor>> result;
21
+ for (const auto &descriptor : range) {
22
+ auto descPtr = std::make_shared<Descriptor>(descriptor, owner);
23
+ result.emplace(descPtr->uuid(), std::move(descPtr));
24
+ }
25
+ return result;
26
+ }
27
+
28
+ Descriptor() = delete;
29
+ Descriptor(BluetoothUUID uuid, Owner *owner);
30
+ Descriptor(const SimpleBLE::Descriptor &descriptor, Owner *owner);
31
+
32
+ [[nodiscard]] Object self() const;
33
+
34
+ [[nodiscard]] const Owner *owner() const { return _owner; }
35
+ constexpr Owner *owner() { return _owner; }
36
+
37
+ [[nodiscard]] const Characteristic *characteristic() const { return _owner; }
38
+
39
+ constexpr Characteristic *characteristic() { return _owner; }
40
+
41
+ [[nodiscard]] constexpr const BluetoothUUID &uuid() const { return _uuid; }
42
+
43
+ // [[nodiscard]] constexpr ResourceUniqueIdentifier<Descriptor> full_resource_identifier() const;
44
+ // [[nodiscard]] constexpr BluetoothUUID this_resource_identifier() const;
45
+
46
+ ByteArray read();
47
+ void write(const ByteArray &data);
48
+
49
+ [[nodiscard]] constexpr std::string to_s() const {
50
+ return Utils::basic_object_inspect_start(*this) + " " + uuid() + ">";
51
+ }
52
+ [[nodiscard]] Rice::String inspect() const;
53
+
54
+ [[nodiscard]] constexpr auto operator<=>(const Descriptor &other) const;
55
+
56
+ };
57
+
58
+
59
+ constexpr auto Descriptor::operator<=>(const Descriptor &other) const {
60
+ return uuid() <=> other.uuid();
61
+ }
62
+
63
+ } // RuBLE
64
+
65
+ constexpr std::ostream &operator<<(std::ostream &os, const RuBLE::Descriptor &d) { return os << d.to_s(); }
66
+
67
+
68
+
69
+
@@ -0,0 +1,212 @@
1
+ #include "types/declarations.hpp"
2
+ #include "containers/NamedBitSet.hpp"
3
+ #include "bindings/Peripheral.hpp"
4
+ #include "management/RubyQueue.hpp"
5
+ #include "containers/Callback.hpp"
6
+ #include "bindings/Service.hpp"
7
+ #include "management/Registry.hpp"
8
+ #include "management/RegistryFactory.hpp"
9
+
10
+ namespace RuBLE {
11
+ Peripheral::Peripheral(const SimpleBLE::Peripheral &peripheral,
12
+ Owner *owner) :
13
+ _owner(owner),
14
+ _peripheral(std::make_shared<SimpleBLE::Peripheral>(peripheral)),
15
+ _addr(_peripheral->address()),
16
+ _addr_type(_peripheral->address_type()),
17
+ _on_connected(std::make_shared<Callback>(1)),
18
+ _on_disconnected(std::make_shared<Callback>(1)),
19
+ _self(Peripheral_DO(*this)),
20
+ _service_registry(serviceRegistryFactory->registry(this)) {
21
+ _peripheral->set_callback_on_connected([this]() {
22
+ RubyQueue::FnType fn = [this]() -> void { _on_connected->fire(self()); };
23
+ if (_on_connected) rubyQueue->push(fn);
24
+ });
25
+ _peripheral->set_callback_on_disconnected([this]() {
26
+ RubyQueue::FnType fn = [this]() -> void { _on_disconnected->fire(self()); };
27
+ if (_on_disconnected) rubyQueue->push(fn);
28
+ });
29
+ }
30
+
31
+ bool Peripheral::initialized() const { return _peripheral->initialized(); }
32
+
33
+ std::string Peripheral::identifier() const { return _peripheral->identifier(); }
34
+
35
+
36
+ std::map<uint16_t, ByteArray> Peripheral::manufacturer_data() const {
37
+ auto data = _peripheral->manufacturer_data();
38
+ return { data.begin(), data.end() };
39
+ }
40
+
41
+ Object Peripheral::self() const {
42
+ return _self;
43
+ }
44
+
45
+ const Peripheral::Owner *Peripheral::owner() const { return _owner; }
46
+ Peripheral::Owner *Peripheral::owner() { return _owner; }
47
+ SimpleBLE::Peripheral &Peripheral::get() { return *_peripheral; }
48
+ const SimpleBLE::Peripheral &Peripheral::get() const { return *_peripheral; }
49
+
50
+ bool Peripheral::is_connected() const { return _peripheral->is_connected(); }
51
+
52
+ bool Peripheral::is_connectable() const { return _peripheral->is_connectable(); }
53
+
54
+ bool Peripheral::is_paired() const { return _peripheral->is_paired(); }
55
+
56
+ Peripheral::StatusFlagSet Peripheral::status_flags() const {
57
+ using StatusFlagTestMap = std::unordered_map<std::string, std::function<bool(const Peripheral&)>>;
58
+ static const StatusFlagTestMap status_flag_testers {
59
+ {"initialized"s, &Peripheral::initialized},
60
+ {"paired"s, &Peripheral::is_paired},
61
+ {"connected"s, &Peripheral::is_connected},
62
+ {"connectable"s, &Peripheral::is_connectable}
63
+ };
64
+ return { status_flag_testers, *this };
65
+ }
66
+
67
+ std::vector<std::string_view> Peripheral::statuses() const {
68
+ return status_flags().flag_names();
69
+ }
70
+
71
+ int16_t Peripheral::rssi() const { return _peripheral->rssi(); }
72
+
73
+ int16_t Peripheral::tx_power() const { return _peripheral->tx_power(); }
74
+
75
+ uint16_t Peripheral::mtu() const { return _peripheral->mtu(); }
76
+
77
+ void Peripheral::connect() { _peripheral->connect(); }
78
+
79
+ void Peripheral::disconnect() { _peripheral->disconnect(); }
80
+
81
+ void Peripheral::unpair() { _peripheral->unpair(); }
82
+
83
+ const std::map<BluetoothUUID, std::shared_ptr<Service>> &Peripheral::services() const {
84
+ if (!rb_during_gc()) {
85
+ // we can't create new services during GC
86
+ for (const auto &c : _peripheral->services()) _service_registry->fetch(c);
87
+ }
88
+ return _service_registry->data();
89
+ // std::vector<SimpleBLE::Service> unwrappedServices = _peripheral->services();
90
+ // auto objects = _service_registry->map_to_objects(unwrappedServices);
91
+ // std::function<BluetoothUUID(const Service&)> mapFn = &Service::uuid;
92
+ // std::map<BluetoothUUID, std::shared_ptr<Service>> result =
93
+ // map_by<BluetoothUUID, std::shared_ptr<Service>>(objects, mapFn);
94
+ // return result;
95
+ }
96
+
97
+ std::shared_ptr<Service> Peripheral::operator[](const BluetoothUUID &svcUuid) const {
98
+ // only check SimpleBLE backend for descriptor if not already instantiated
99
+ try {
100
+ if (_service_registry || !_service_registry->contains(svcUuid)) const auto _result = services();
101
+ return _service_registry->at(svcUuid);
102
+ } catch (std::out_of_range &ex) {
103
+ return {};
104
+ }
105
+ }
106
+
107
+ ByteArray Peripheral::read(const BluetoothUUID &service, const BluetoothUUID &characteristic) {
108
+ return _peripheral->read(service, characteristic);
109
+ }
110
+
111
+ void Peripheral::write_request(const BluetoothUUID &service, const BluetoothUUID &characteristic,
112
+ const ByteArray &data) {
113
+ return _peripheral->write_request(service, characteristic, data);
114
+ }
115
+
116
+ void Peripheral::write_command(const BluetoothUUID &service, const BluetoothUUID &characteristic,
117
+ const ByteArray &data) {
118
+ return _peripheral->write_command(service, characteristic, data);
119
+ }
120
+
121
+ void Peripheral::notify(const BluetoothUUID &service, const BluetoothUUID &characteristic,
122
+ std::function<void(ByteArray)> callback) {
123
+ // FIXME: make sure we don't have a GC problem with this callback
124
+ return _peripheral->notify(service, characteristic, callback);
125
+ }
126
+
127
+ void Peripheral::indicate(const BluetoothUUID &service, const BluetoothUUID &characteristic,
128
+ std::function<void(ByteArray)> callback) {
129
+ // FIXME: make sure we don't have a GC problem with this callback
130
+ return _peripheral->indicate(service, characteristic, callback);
131
+ }
132
+
133
+ void Peripheral::unsubscribe(const BluetoothUUID &service, const BluetoothUUID &characteristic) {
134
+ return _peripheral->unsubscribe(service, characteristic);
135
+ }
136
+
137
+ ByteArray Peripheral::read(const BluetoothUUID &service, const BluetoothUUID &characteristic,
138
+ const BluetoothUUID &descriptor) {
139
+ return _peripheral->read(service, characteristic, descriptor);
140
+ }
141
+
142
+ void Peripheral::write(const BluetoothUUID &service, const BluetoothUUID &characteristic,
143
+ const BluetoothUUID &descriptor, const ByteArray &data) {
144
+ return _peripheral->write(service, characteristic, descriptor, data);
145
+ }
146
+
147
+ //bool Peripheral::operator==(const Peripheral &p) const {
148
+ // return _peripheral && _peripheral->underlying() == p.underlying();
149
+ //}
150
+
151
+ //bool Peripheral::operator==(const Peripheral &p) const { return _peripheral == p._peripheral; }
152
+
153
+ void Peripheral::on_connect(Object on_connect) {
154
+ _on_connected->set(on_connect);
155
+ }
156
+
157
+ void Peripheral::on_disconnect(Object on_disconnect) {
158
+ _on_disconnected->set(on_disconnect);
159
+ }
160
+
161
+ std::string Peripheral::to_s() const {
162
+ std::ostringstream oss;
163
+ oss << Utils::basic_object_inspect_start(*this) << " ";
164
+ if (!initialized()) return oss.str() + "uninitialized>";
165
+ oss << "@address[" << Utils::enum_val_as_string(address_type()) << "]=\"" << address() << "\" ";
166
+ oss << "@identifier=\"" << identifier() << "\" ";
167
+ oss << "@rssi=" << rssi() << " ";
168
+ oss << "@tx_power=" << tx_power() << " ";
169
+ oss << "@mtu=" << mtu() << " ";
170
+ oss << "svc_count=" << _peripheral->services().size() << " ";
171
+ oss << Utils::join(statuses(), "|");
172
+ // std::map<uint16_t, SimpleBLE::ByteArray> manufacturer_data() const;
173
+ oss << ">";
174
+ return oss.str();
175
+ }
176
+
177
+ void Peripheral::ruby_mark() const {
178
+ rb_gc_mark(self());
179
+ if (_on_connected) _on_connected->ruby_mark();
180
+ if (_on_disconnected) _on_disconnected->ruby_mark();
181
+ Rice::ruby_mark(_service_registry.get());
182
+ }
183
+
184
+
185
+ void Init_Peripheral() {
186
+ define_class_under<SimpleBLE::Peripheral>(rb_mRuBLEUnderlying, "Peripheral");
187
+ rb_cPeripheral = define_class_under<Peripheral>(rb_mRuBLE, "Peripheral")
188
+ // .define_constructor(Constructor<Peripheral>())
189
+ .define_method("inspect", &Peripheral::to_s)
190
+ .define_method("initialized?", &Peripheral::initialized)
191
+ .define_method("identifier", &Peripheral::identifier)
192
+ .define_method("address", &Peripheral::address) // returns BluetoothAddress (alias for string)
193
+ .define_method("address_type", &Peripheral::address_type)
194
+ .define_method("rssi", &Peripheral::rssi)
195
+ .define_method("tx_power", &Peripheral::tx_power)
196
+ .define_method("mtu", &Peripheral::mtu)
197
+ .define_method("connect", &Peripheral::connect)
198
+ .define_method("disconnect", &Peripheral::disconnect)
199
+ .define_method("connected?", &Peripheral::is_connected)
200
+ .define_method("connectable?", &Peripheral::is_connectable)
201
+ .define_method("paired?", &Peripheral::is_paired)
202
+ .define_method("unpair", &Peripheral::unpair)
203
+ // TODO: this isn't returning any useful data. Should we keep it? (Or is there a way to parse?)
204
+ .define_method("manufacturer_data", &Peripheral::manufacturer_data)
205
+ .define_method("on_connect", &Peripheral::on_connect, Arg("cb").keepAlive())
206
+ .define_method("on_disconnect", &Peripheral::on_disconnect, Arg("cb").keepAlive())
207
+ .define_singleton_attr("unpair_all_on_exit", &Peripheral::unpair_all_on_exit, Rice::AttrAccess::ReadWrite)
208
+ ;
209
+ define_class_under<std::shared_ptr<RuBLE::Peripheral>>(rb_mRuBLE, "PeripheralPtr");
210
+ }
211
+ }
212
+