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