ruble 0.0.3.alpha
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gdbinit +21 -0
- data/.gitignore +18 -0
- data/.rubocop.yml +96 -0
- data/.ruby-version +1 -0
- data/CHANGELOG.md +6 -0
- data/CMakeLists.txt +4 -0
- data/CODE_OF_CONDUCT.md +84 -0
- data/Gemfile +21 -0
- data/Gemfile.lock +98 -0
- data/LICENSE.txt +21 -0
- data/README.md +63 -0
- data/Rakefile +41 -0
- data/ext/ruble/.gitignore +5 -0
- data/ext/ruble/CMakeLists.txt +157 -0
- data/ext/ruble/RuBLEHelpers.cmake +240 -0
- data/ext/ruble/bindings/Adapter.cpp +193 -0
- data/ext/ruble/bindings/Adapter.hpp +85 -0
- data/ext/ruble/bindings/Characteristic.cpp +171 -0
- data/ext/ruble/bindings/Characteristic.hpp +132 -0
- data/ext/ruble/bindings/Descriptor.cpp +34 -0
- data/ext/ruble/bindings/Descriptor.hpp +69 -0
- data/ext/ruble/bindings/Peripheral.cpp +212 -0
- data/ext/ruble/bindings/Peripheral.hpp +108 -0
- data/ext/ruble/bindings/RuBLE.cpp +115 -0
- data/ext/ruble/bindings/Service.cpp +112 -0
- data/ext/ruble/bindings/Service.hpp +61 -0
- data/ext/ruble/bindings/common.hpp +48 -0
- data/ext/ruble/bindings/globals.cpp +43 -0
- data/ext/ruble/cmake.mk +62 -0
- data/ext/ruble/concerns/CharacteristicValueTracker.cpp +18 -0
- data/ext/ruble/concerns/CharacteristicValueTracker.hpp +40 -0
- data/ext/ruble/concerns/Rubyable.cpp +4 -0
- data/ext/ruble/concerns/Rubyable.hpp +46 -0
- data/ext/ruble/config.h.in +25 -0
- data/ext/ruble/containers/ByteArray.cpp +64 -0
- data/ext/ruble/containers/ByteArray.hpp +161 -0
- data/ext/ruble/containers/Callback.hpp +52 -0
- data/ext/ruble/containers/NamedBitSet.hpp +140 -0
- data/ext/ruble/containers/NamedBitSet.ipp +71 -0
- data/ext/ruble/extconf.rb +30 -0
- data/ext/ruble/management/Registry.cpp +63 -0
- data/ext/ruble/management/Registry.hpp +170 -0
- data/ext/ruble/management/RegistryFactory.hpp +113 -0
- data/ext/ruble/management/RubyQueue.cpp +152 -0
- data/ext/ruble/management/RubyQueue.hpp +69 -0
- data/ext/ruble/modularize.diff +28 -0
- data/ext/ruble/types/SimpleBLE.hpp +21 -0
- data/ext/ruble/types/declarations.hpp +91 -0
- data/ext/ruble/types/helpers.hpp +12 -0
- data/ext/ruble/types/ruby.hpp +36 -0
- data/ext/ruble/types/stl.hpp +41 -0
- data/ext/ruble/utils/RubyCallbackTraits.cpp +28 -0
- data/ext/ruble/utils/RubyCallbackTraits.hpp +48 -0
- data/ext/ruble/utils/async.cpp +10 -0
- data/ext/ruble/utils/async.hpp +76 -0
- data/ext/ruble/utils/containers.hpp +41 -0
- data/ext/ruble/utils/exception_handling.cpp +50 -0
- data/ext/ruble/utils/exception_handling.hpp +53 -0
- data/ext/ruble/utils/garbage_collection.cpp +82 -0
- data/ext/ruble/utils/garbage_collection.hpp +22 -0
- data/ext/ruble/utils/hash.cpp +83 -0
- data/ext/ruble/utils/hash.hpp +52 -0
- data/ext/ruble/utils/hexadecimal.hpp +116 -0
- data/ext/ruble/utils/human_type_names.hpp +38 -0
- data/ext/ruble/utils/inspection.cpp +24 -0
- data/ext/ruble/utils/inspection.hpp +108 -0
- data/ext/ruble/utils/ruby.hpp +103 -0
- data/ext/ruble/utils/ruby_context.hpp +73 -0
- data/lib/ruble/build/.rubocop.yml +19 -0
- data/lib/ruble/build/boost.rb +34 -0
- data/lib/ruble/build/cmake.rb +134 -0
- data/lib/ruble/build/core_ext.rb +5 -0
- data/lib/ruble/build/data/bundler.rb +24 -0
- data/lib/ruble/build/data/extension.rb +101 -0
- data/lib/ruble/build/data/os.rb +21 -0
- data/lib/ruble/build/data/rice.rb +24 -0
- data/lib/ruble/build/data.rb +22 -0
- data/lib/ruble/build/extconf.rb +76 -0
- data/lib/ruble/build/github_repo.rb +129 -0
- data/lib/ruble/build/simpleble.rb +56 -0
- data/lib/ruble/build.rb +28 -0
- data/lib/ruble/version.rb +7 -0
- data/lib/ruble.rb +46 -0
- data/lib/tasks/dev/dev_tasks.rb +130 -0
- data/lib/tasks/dev/pager.rb +218 -0
- data/lib/tasks/dev/paths.rb +30 -0
- data/lib/tasks/dev/state_hash.rb +65 -0
- data/lib/tasks/dev.rake +41 -0
- data/lib/tasks/simpleble.rake +29 -0
- data/sig/rubble.rbs +4 -0
- 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
|
+
|