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,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
+