ruble 0.0.3.alpha

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (92) hide show
  1. checksums.yaml +7 -0
  2. data/.gdbinit +21 -0
  3. data/.gitignore +18 -0
  4. data/.rubocop.yml +96 -0
  5. data/.ruby-version +1 -0
  6. data/CHANGELOG.md +6 -0
  7. data/CMakeLists.txt +4 -0
  8. data/CODE_OF_CONDUCT.md +84 -0
  9. data/Gemfile +21 -0
  10. data/Gemfile.lock +98 -0
  11. data/LICENSE.txt +21 -0
  12. data/README.md +63 -0
  13. data/Rakefile +41 -0
  14. data/ext/ruble/.gitignore +5 -0
  15. data/ext/ruble/CMakeLists.txt +157 -0
  16. data/ext/ruble/RuBLEHelpers.cmake +240 -0
  17. data/ext/ruble/bindings/Adapter.cpp +193 -0
  18. data/ext/ruble/bindings/Adapter.hpp +85 -0
  19. data/ext/ruble/bindings/Characteristic.cpp +171 -0
  20. data/ext/ruble/bindings/Characteristic.hpp +132 -0
  21. data/ext/ruble/bindings/Descriptor.cpp +34 -0
  22. data/ext/ruble/bindings/Descriptor.hpp +69 -0
  23. data/ext/ruble/bindings/Peripheral.cpp +212 -0
  24. data/ext/ruble/bindings/Peripheral.hpp +108 -0
  25. data/ext/ruble/bindings/RuBLE.cpp +115 -0
  26. data/ext/ruble/bindings/Service.cpp +112 -0
  27. data/ext/ruble/bindings/Service.hpp +61 -0
  28. data/ext/ruble/bindings/common.hpp +48 -0
  29. data/ext/ruble/bindings/globals.cpp +43 -0
  30. data/ext/ruble/cmake.mk +62 -0
  31. data/ext/ruble/concerns/CharacteristicValueTracker.cpp +18 -0
  32. data/ext/ruble/concerns/CharacteristicValueTracker.hpp +40 -0
  33. data/ext/ruble/concerns/Rubyable.cpp +4 -0
  34. data/ext/ruble/concerns/Rubyable.hpp +46 -0
  35. data/ext/ruble/config.h.in +25 -0
  36. data/ext/ruble/containers/ByteArray.cpp +64 -0
  37. data/ext/ruble/containers/ByteArray.hpp +161 -0
  38. data/ext/ruble/containers/Callback.hpp +52 -0
  39. data/ext/ruble/containers/NamedBitSet.hpp +140 -0
  40. data/ext/ruble/containers/NamedBitSet.ipp +71 -0
  41. data/ext/ruble/extconf.rb +30 -0
  42. data/ext/ruble/management/Registry.cpp +63 -0
  43. data/ext/ruble/management/Registry.hpp +170 -0
  44. data/ext/ruble/management/RegistryFactory.hpp +113 -0
  45. data/ext/ruble/management/RubyQueue.cpp +152 -0
  46. data/ext/ruble/management/RubyQueue.hpp +69 -0
  47. data/ext/ruble/modularize.diff +28 -0
  48. data/ext/ruble/types/SimpleBLE.hpp +21 -0
  49. data/ext/ruble/types/declarations.hpp +91 -0
  50. data/ext/ruble/types/helpers.hpp +12 -0
  51. data/ext/ruble/types/ruby.hpp +36 -0
  52. data/ext/ruble/types/stl.hpp +41 -0
  53. data/ext/ruble/utils/RubyCallbackTraits.cpp +28 -0
  54. data/ext/ruble/utils/RubyCallbackTraits.hpp +48 -0
  55. data/ext/ruble/utils/async.cpp +10 -0
  56. data/ext/ruble/utils/async.hpp +76 -0
  57. data/ext/ruble/utils/containers.hpp +41 -0
  58. data/ext/ruble/utils/exception_handling.cpp +50 -0
  59. data/ext/ruble/utils/exception_handling.hpp +53 -0
  60. data/ext/ruble/utils/garbage_collection.cpp +82 -0
  61. data/ext/ruble/utils/garbage_collection.hpp +22 -0
  62. data/ext/ruble/utils/hash.cpp +83 -0
  63. data/ext/ruble/utils/hash.hpp +52 -0
  64. data/ext/ruble/utils/hexadecimal.hpp +116 -0
  65. data/ext/ruble/utils/human_type_names.hpp +38 -0
  66. data/ext/ruble/utils/inspection.cpp +24 -0
  67. data/ext/ruble/utils/inspection.hpp +108 -0
  68. data/ext/ruble/utils/ruby.hpp +103 -0
  69. data/ext/ruble/utils/ruby_context.hpp +73 -0
  70. data/lib/ruble/build/.rubocop.yml +19 -0
  71. data/lib/ruble/build/boost.rb +34 -0
  72. data/lib/ruble/build/cmake.rb +134 -0
  73. data/lib/ruble/build/core_ext.rb +5 -0
  74. data/lib/ruble/build/data/bundler.rb +24 -0
  75. data/lib/ruble/build/data/extension.rb +101 -0
  76. data/lib/ruble/build/data/os.rb +21 -0
  77. data/lib/ruble/build/data/rice.rb +24 -0
  78. data/lib/ruble/build/data.rb +22 -0
  79. data/lib/ruble/build/extconf.rb +76 -0
  80. data/lib/ruble/build/github_repo.rb +129 -0
  81. data/lib/ruble/build/simpleble.rb +56 -0
  82. data/lib/ruble/build.rb +28 -0
  83. data/lib/ruble/version.rb +7 -0
  84. data/lib/ruble.rb +46 -0
  85. data/lib/tasks/dev/dev_tasks.rb +130 -0
  86. data/lib/tasks/dev/pager.rb +218 -0
  87. data/lib/tasks/dev/paths.rb +30 -0
  88. data/lib/tasks/dev/state_hash.rb +65 -0
  89. data/lib/tasks/dev.rake +41 -0
  90. data/lib/tasks/simpleble.rake +29 -0
  91. data/sig/rubble.rbs +4 -0
  92. metadata +263 -0
@@ -0,0 +1,240 @@
1
+ # From https://stackoverflow.com/a/51987470
2
+ # Get all properties that cmake supports
3
+ execute_process(COMMAND cmake --help-property-list OUTPUT_VARIABLE CMAKE_PROPERTY_LIST)
4
+
5
+ # Convert command output into a CMake list
6
+ STRING(REGEX REPLACE ";" "\\\\;" CMAKE_PROPERTY_LIST "${CMAKE_PROPERTY_LIST}")
7
+ STRING(REGEX REPLACE "\n" ";" CMAKE_PROPERTY_LIST "${CMAKE_PROPERTY_LIST}")
8
+ # Fix https://stackoverflow.com/questions/32197663/how-can-i-remove-the-the-location-property-may-not-be-read-from-target-error-i
9
+ list(FILTER CMAKE_PROPERTY_LIST EXCLUDE REGEX "^LOCATION$|^LOCATION_|_LOCATION$")
10
+ # For some reason, "TYPE" shows up twice - others might too?
11
+ list(REMOVE_DUPLICATES CMAKE_PROPERTY_LIST)
12
+
13
+ # build whitelist by filtering down from CMAKE_PROPERTY_LIST in case cmake is
14
+ # a different version, and one of our hardcoded whitelisted properties
15
+ # doesn't exist!
16
+ unset(CMAKE_WHITELISTED_PROPERTY_LIST)
17
+ foreach(prop ${CMAKE_PROPERTY_LIST})
18
+ if(prop MATCHES "^(INTERFACE|[_a-z]|IMPORTED_LIBNAME_|MAP_IMPORTED_CONFIG_)|^(COMPATIBLE_INTERFACE_(BOOL|NUMBER_MAX|NUMBER_MIN|STRING)|EXPORT_NAME|IMPORTED(_GLOBAL|_CONFIGURATIONS|_LIBNAME)?|NAME|TYPE|NO_SYSTEM_FROM_IMPORTED)$")
19
+ list(APPEND CMAKE_WHITELISTED_PROPERTY_LIST ${prop})
20
+ endif()
21
+ endforeach(prop)
22
+
23
+ function(print_properties)
24
+ message ("CMAKE_PROPERTY_LIST = ${CMAKE_PROPERTY_LIST}")
25
+ endfunction(print_properties)
26
+
27
+ function(print_whitelisted_properties)
28
+ message ("CMAKE_WHITELISTED_PROPERTY_LIST = ${CMAKE_WHITELISTED_PROPERTY_LIST}")
29
+ endfunction(print_whitelisted_properties)
30
+
31
+ function(print_target_properties tgt)
32
+ if(NOT TARGET ${tgt})
33
+ message("There is no target named '${tgt}'")
34
+ return()
35
+ endif()
36
+
37
+ get_target_property(target_type ${tgt} TYPE)
38
+ if(target_type STREQUAL "INTERFACE_LIBRARY")
39
+ set(PROP_LIST ${CMAKE_WHITELISTED_PROPERTY_LIST})
40
+ else()
41
+ set(PROP_LIST ${CMAKE_PROPERTY_LIST})
42
+ endif()
43
+
44
+ foreach (prop ${PROP_LIST})
45
+ string(REPLACE "<CONFIG>" "${CMAKE_BUILD_TYPE}" prop ${prop})
46
+ # message ("Checking ${prop}")
47
+ get_property(propval TARGET ${tgt} PROPERTY ${prop} SET)
48
+ if (propval)
49
+ get_target_property(propval ${tgt} ${prop})
50
+ message ("${tgt} ${prop} = ${propval}")
51
+ endif()
52
+ endforeach(prop)
53
+ endfunction(print_target_properties)
54
+
55
+ function(dump_variables)
56
+ get_cmake_property(_variableNames VARIABLES)
57
+ list (SORT _variableNames)
58
+ foreach (_variableName ${_variableNames})
59
+ message(STATUS "${_variableName}=${${_variableName}}")
60
+ endforeach()
61
+ endfunction()
62
+
63
+ macro(build_boost)
64
+ set(REPO "https://github.com/boostorg/boost")
65
+ set(TAG boost-1.83.0)
66
+ cmake_path(SET CACHE_LOCATION NORMALIZE "${CMAKE_SOURCE_DIR}/../../tmp/${TAG}")
67
+
68
+ set(ADDITIONAL_DEPENDENCIES core;stacktrace)
69
+ set(HEADER_ONLY_LIBS headers;describe)
70
+ set(LINKED_LIBS exception;endian;algorithm) # + detected stacktrace backend
71
+ set(STACKTRACE_BACKEND_PRIORITIES backtrace;addr2line;windgb_cached;windbg;basic;noop)
72
+
73
+ set(ALL_LIBS ${HEADER_ONLY_LIBS};${LINKED_LIBS})
74
+ set(BUILD_DEPS ${ALL_LIBS})
75
+ set(BOOST_INCLUDE_LIBRARIES ${ADDITIONAL_DEPENDENCIES};${BUILD_DEPS})
76
+
77
+ list(TRANSFORM HEADER_ONLY_LIBS PREPEND "Boost::"
78
+ OUTPUT_VARIABLE HEADER_ONLY_NAMESPACES)
79
+ list(TRANSFORM LINKED_LIBS PREPEND "Boost::"
80
+ OUTPUT_VARIABLE LINKED_NAMESPACES)
81
+ list(TRANSFORM BUILD_DEPS PREPEND "boost_"
82
+ OUTPUT_VARIABLE PREFIXED_BUILD_DEPS)
83
+ # dump_variables()
84
+ # message(FATAL_ERROR "quitting")
85
+
86
+ set(STACKTRACE_BACKEND_PRIORITIES backtrace;addr2line;windgb_cached;windbg;basic;noop)
87
+ list(TRANSFORM STACKTRACE_BACKEND_PRIORITIES
88
+ PREPEND "stacktrace_"
89
+ OUTPUT_VARIABLE STACKTRACE_BACKENDS)
90
+
91
+ # set(USES_TERMINAL_DOWNLOAD ON)
92
+ # set(USES_TERMINAL_UPDATE ON)
93
+ # set(LOG_DOWNLOAD ON)
94
+ # set(LOG_UPDATE ON)
95
+
96
+ set(BOOST_ENABLE_PYTHON OFF)
97
+ set(BUILD_SHARED_LIBS OFF)
98
+ set(Boost_DEBUG OFF)
99
+ set(Boost_VERBOSE OFF)
100
+ set(BUILD_TESTING OFF)
101
+
102
+ # TODO: add option to use local boost (in which case we can link dynamically)
103
+
104
+ include(FetchContent)
105
+ # cmake_policy(SET CMP0097 NEW)
106
+ FetchContent_Declare(
107
+ Boost
108
+ SOURCE_DIR "${CACHE_LOCATION}"
109
+ URL "${rb_boost_asset_browser_download_url}"
110
+ # GIT_REPOSITORY "${REPO}" # TODO: would an archive make more sense(?)
111
+ # GIT_TAG "${TAG}" # TODO: set this in build-config.cmake
112
+ # GIT_PROGRESS ON
113
+ # GIT_SHALLOW ON
114
+ # GIT_SUBMODULES ""
115
+ # GIT_SUBMODULES_RECURSE OFF
116
+ # GIT_REMOTE_UPDATE_STRATEGY REBASE
117
+ # UPDATE_COMMAND git submodule update --init --recursive --remote -f --depth 1 -j ${NPROC}
118
+ DOWNLOAD_EXTRACT_TIMESTAMP ON
119
+ USES_TERMINAL_DOWNLOAD ON
120
+ USES_TERMINAL_UPDATE ON
121
+ OVERRIDE_FIND_PACKAGE
122
+ )
123
+ FetchContent_MakeAvailable(Boost)
124
+ # dump_variables()
125
+
126
+ find_package(
127
+ Boost
128
+ REQUIRED
129
+ ${BOOST_INCLDUE_LIBRARIES}
130
+ OPTIONAL_COMPONENTS
131
+ ${STACKTRACE_BACKENDS}
132
+ PATHS "${CACHE_LOCATION}" "${CACHE_LOCATION}/tools/cmake"
133
+ EXCLUDE_FROM_ALL ON
134
+ NO_DEFAULT_PATH
135
+ )
136
+ endmacro()
137
+
138
+ # TODO: Should we fall back with this?
139
+ function(setup_boost build_target)
140
+ build_boost()
141
+
142
+ target_link_libraries(${build_target} INTERFACE ${HEADER_ONLY_NAMESPACES}) #Boost::headers Boost::exception Boost::describe Boost::endian)
143
+ target_link_libraries(${build_target} PRIVATE ${LINKED_NAMESPACES})
144
+ add_dependencies(${build_target} ${PREFIXED_BUILD_DEPS})
145
+
146
+ foreach(possible_backend IN LISTS STACKTRACE_BACKEND_PRIORITIES)
147
+ string(TOUPPER "${possible_backend}" possible_backend_upper)
148
+
149
+ message(STATUS "Testing for stacktrace backend ${possible_backend}")
150
+ message(STATUS "BOOST_STACKTRACE_ENABLE_${possible_backend_upper}: ${BOOST_STACKTRACE_ENABLE_${possible_backend_upper}}")
151
+ message(STATUS "BOOST_STACKTRACE_HAS_${possible_backend_upper}: ${BOOST_STACKTRACE_HAS_${possible_backend_upper}}")
152
+ if(NOT BOOST_STACKTRACE_ENABLE_${possible_backend_upper} OR NOT BOOST_STACKTRACE_HAS_${possible_backend_upper})
153
+ continue()
154
+ endif()
155
+
156
+ string(REGEX MATCH "^windbg_" STACKTRACE_BACKEND_IS_WINDBG "${possible_backend}")
157
+ set(STACKTRACE_BACKEND "${possible_backend}")
158
+ set(STACKTRACE_BACKEND "${possible_backend}" PARENT_SCOPE)
159
+ set(BOOST_STACKTRACE_LINK ON)
160
+ set(BOOST_STACKTRACE_LINK ON PARENT_SCOPE)
161
+ set(BOOST_STACKTRACE_USE_${possible_backend_upper} ON PARENT_SCOPE)
162
+
163
+ target_link_libraries(${build_target} PRIVATE Boost::stacktrace_${STACKTRACE_BACKEND})
164
+ add_dependencies(${build_target} boost_stacktrace_${STACKTRACE_BACKEND})
165
+
166
+ target_link_libraries(${build_target} PRIVATE Boost::stacktrace_${possible_backend})
167
+ if(possible_backend STREQUAL "backtrace")
168
+ include(FindBacktrace)
169
+ if(Backtrace_LIBRARY)
170
+ target_link_libraries(${build_target} PRIVATE ${Backtrace_LIBRARY})
171
+ endif()
172
+
173
+ if(Backtrace_HEADER)
174
+ set(BACKTRACE_HEADER_PATH "${Backtrace_INCLUDE_DIR}/${Backtrace_HEADER}")
175
+ set(BOOST_STACKTRACE_BACKTRACE_INCLUDE_FILE "${BACKTRACE_HEADER_PATH}" PARENT_SCOPE)
176
+ endif ()
177
+ # elseif(possible_backend STREQUAL "addr2line")
178
+ # # This really only makes sense to use if this is being built on the computer it's used on
179
+ # set(BOOST_STACKTRACE_ADDR2LINE_LOCATION "${CMAKE_ADDR2LINE}")
180
+ elseif(WINDOWS AND STACKTRACE_BACKEND_IS_WINDBG) # TODO: untested
181
+ find_library(OLE32 ole32)
182
+ find_library(WINDBGENG dbgeng)
183
+ target_link_libraries(${build_target} PUBLIC ole32 dbgeng)
184
+ endif()
185
+ break()
186
+ endforeach()
187
+
188
+ if(NOT BOOST_STACKTRACE_LINK)
189
+ # dump_variables()
190
+ message(FATAL_ERROR "Could not find supported Boost::stacktrace backend")
191
+ endif()
192
+ message(STATUS "Boost::stacktrace backend: ${STACKTRACE_BACKEND}")
193
+
194
+ set(UNUSED_STACKTRACE_BACKENDS ${STACKTRACE_BACKEND_PRIORITIES})
195
+ list(REMOVE_ITEM UNUSED_STACKTRACE_BACKENDS "${STACKTRACE_BACKEND}")
196
+ message(VERBOSE "Disabling unused Boost::stacktrace backends: ${UNUSED_STACKTRACE_BACKENDS}")
197
+ foreach(unused_backend IN LISTS UNUSED_STACKTRACE_BACKENDS)
198
+ string(TOUPPER "${unused_backend}" unused_backend_upper)
199
+ if(NOT BOOST_STACKTRACE_ENABLE_${unused_backend_upper})
200
+ continue()
201
+ endif()
202
+
203
+ set_target_properties(boost_stacktrace_${unused_backend} PROPERTIES EXCLUDE_FROM_ALL ON)
204
+ set(BOOST_STACKTRACE_ENABLE_${unused_backend_upper} OFF)
205
+ endforeach()
206
+ endfunction()
207
+
208
+ # TODO: try changing these into functions
209
+ function(setup_simpleble build_target)
210
+ include(FetchContent)
211
+ # TODO: would it be easier/better to use git and the commit hash
212
+ # and build SimpleBLE from source? The main downside is more
213
+ # dependencies. The upside is vastly simplifying configuring
214
+ # simpleble_DIR, as well as choosing between shared/static linking.
215
+ FetchContent_Declare(
216
+ simpleble
217
+ URL "${rb_simpleble_asset_browser_download_url}"
218
+ DOWNLOAD_EXTRACT_TIMESTAMP ON
219
+ # OVERRIDE_FIND_PACKAGE
220
+ )
221
+ FetchContent_MakeAvailable(simpleble)
222
+ set(simpleble_DIR ${simpleble_SOURCE_DIR}/${rb_simpleble_archive_path})
223
+ find_package(simpleble REQUIRED CONFIG PATHS "${simpleble_DIR}" NO_DEFAULT_PATH)
224
+ target_link_libraries("${build_target}" PUBLIC simpleble::simpleble)
225
+ include("${simpleble_CONFIG}")
226
+ message(STATUS "SimpleBLE found in ${simpleble_DIR}")
227
+ endfunction()
228
+
229
+
230
+ macro(setup_ruby build_target)
231
+ #set(_Ruby_DEBUG_OUTPUT ON)
232
+ set(RUBY_FIND_VERSION 3.2)
233
+ find_package(Ruby 3.2 REQUIRED Ruby_FIND_VIRTUALENV)
234
+ target_link_libraries("${build_target}" PUBLIC ${Ruby_LIBRARIES})
235
+ target_include_directories("${build_target}" PUBLIC ${Ruby_INCLUDE_DIRS})
236
+ target_include_directories("${build_target}" PRIVATE ${RICE_INCLUDE_PATH})
237
+ set_target_properties("${build_target}" PROPERTIES INSTALL_RPATH_USE_LINK_PATH ON)
238
+ message(STATUS "libruby ${Ruby_VERSION} found at ${Ruby_LIBRARY}")
239
+ message(STATUS "Rice ${RICE_VERSION} found in ${RICE_INCLUDE_PATH}")
240
+ endmacro()
@@ -0,0 +1,193 @@
1
+ #include <ranges>
2
+
3
+ #include "types/declarations.hpp"
4
+ #include "bindings/Adapter.hpp"
5
+ #include "bindings/Peripheral.hpp"
6
+ #include "management/Registry.hpp"
7
+ #include "management/RegistryFactory.hpp"
8
+ #include "management/RubyQueue.hpp"
9
+ #include "utils/containers.hpp"
10
+
11
+ namespace RuBLE {
12
+ using Rice::String;
13
+ Adapter::Adapter(const SimpleBLE::Adapter &adapter) :
14
+ _adapter(std::make_shared<SimpleBLE::Adapter>(adapter)),
15
+ _addr(_adapter->address()),
16
+ _peripheral_registry(peripheralRegistryFactory->registry(this)),
17
+ _on_scan_start(std::make_shared<Callback>(1)),
18
+ _on_scan_stop(std::make_shared<Callback>(1)),
19
+ _on_scan_update(std::make_shared<Callback>(2)),
20
+ _on_scan_find(std::make_shared<Callback>(2)),
21
+ _self(Adapter_DO(*this)) {
22
+ get().set_callback_on_scan_start([this]() {
23
+ RubyQueue::FnType fn = [this]() -> void { _on_scan_start->fire(self()); };
24
+ if (_on_scan_start) rubyQueue->push(fn);
25
+ });
26
+
27
+ get().set_callback_on_scan_stop([this]() {
28
+ RubyQueue::FnType fn = [this]() -> void { _on_scan_stop->fire(self()); };
29
+ if (_on_scan_stop) rubyQueue->push(fn);
30
+ });
31
+
32
+ get().set_callback_on_scan_updated([this](const SimpleBLE::Peripheral &peripheral) {
33
+ RubyQueue::FnType fn = [this, peripheral]() -> void {
34
+ const std::shared_ptr<Peripheral> &wrapped = _peripheral_registry->fetch(peripheral);
35
+ _on_scan_update->fire(wrapped->self(), self());
36
+ };
37
+ if (_on_scan_update) rubyQueue->push(fn);
38
+ });
39
+
40
+ get().set_callback_on_scan_found([this](const SimpleBLE::Peripheral &peripheral) {
41
+ RubyQueue::FnType fn = [this, peripheral]() -> void {
42
+ const std::shared_ptr<Peripheral> &wrapped = _peripheral_registry->fetch(peripheral);
43
+ _on_scan_find->fire(wrapped->self(), self());
44
+ };
45
+ if (_on_scan_find) rubyQueue->push(fn);
46
+ });
47
+ }
48
+
49
+ bool Adapter::initialized() const { return get().initialized(); }
50
+
51
+ std::string Adapter::identifier() { return (_adapter) ? get().identifier() : ""; }
52
+
53
+
54
+ BluetoothAddress Adapter::address() const {
55
+ return _addr; /*get().address();*/
56
+ }
57
+
58
+ void Adapter::scan_start() { if (!scan_is_active()) get().scan_start(); }
59
+
60
+ void Adapter::scan_stop() { if (scan_is_active()) get().scan_stop(); }
61
+
62
+ void Adapter::scan_for(int timeout_ms) { get().scan_for(timeout_ms); }
63
+
64
+ bool Adapter::scan_is_active() const {
65
+ return const_cast<Adapter*>(this)->get().scan_is_active();
66
+ }
67
+
68
+ bool Adapter::operator==(const SimpleBLE::BluetoothAddress &addr) const { return !_addr.empty() && _addr == addr; }
69
+
70
+ bool Adapter::operator==(const Adapter &ap) const { return *this == ap._addr; }
71
+
72
+ std::vector<std::shared_ptr<Peripheral>> Adapter::scan_get_results() {
73
+ auto unwrappedPeripherals = get().scan_get_results();
74
+ return _peripheral_registry->map_to_objects(unwrappedPeripherals);
75
+ }
76
+
77
+ Rice::Array Adapter::scan_get_ruby_results() {
78
+ auto unwrappedPeripherals = get().scan_get_results();
79
+ return _peripheral_registry->map_to_ruby_objects(unwrappedPeripherals);
80
+ }
81
+
82
+ std::vector<std::shared_ptr<Peripheral>> Adapter::get_paired_peripherals() {
83
+ auto unwrappedPeripherals = get().get_paired_peripherals();
84
+ return _peripheral_registry->map_to_objects(unwrappedPeripherals);
85
+ }
86
+
87
+
88
+ Rice::Array Adapter::get_ruby_paired_peripherals() {
89
+ auto unwrappedPeripherals = get().get_paired_peripherals();
90
+ return _peripheral_registry->map_to_ruby_objects(unwrappedPeripherals);
91
+ }
92
+
93
+ SimpleBLE::Adapter &Adapter::get() {
94
+ if (!_adapter) throw std::runtime_error("Tried fetching address for unconfigured Adapter instance");
95
+ return *_adapter;
96
+ }
97
+
98
+ const SimpleBLE::Adapter &Adapter::get() const {
99
+ if (!_adapter) throw std::runtime_error("Tried fetching address for unconfigured Adapter instance");
100
+ return *_adapter;
101
+ }
102
+
103
+ Object Adapter::self() const { return _self; }
104
+
105
+ bool Adapter::bluetooth_enabled() { return SimpleBLE::Adapter::bluetooth_enabled(); }
106
+
107
+ std::vector<std::shared_ptr<Adapter>> Adapter::get_adapters() {
108
+ std::vector<SimpleBLE::Adapter> unwrappedAdapters = SimpleBLE::Adapter::get_adapters();
109
+ return adapterRegistry->map_to_objects(unwrappedAdapters);
110
+ }
111
+
112
+ Rice::Array Adapter::get_ruby_adapters() {
113
+ std::vector<SimpleBLE::Adapter> unwrappedAdapters = SimpleBLE::Adapter::get_adapters();
114
+ return adapterRegistry->map_to_ruby_objects(unwrappedAdapters);
115
+ }
116
+
117
+ void Adapter::on_scan_start(Object new_cb) { _on_scan_start->set(new_cb); }
118
+
119
+ void Adapter::on_scan_stop(Object new_cb) { _on_scan_stop->set(new_cb); }
120
+
121
+ void Adapter::on_scan_update(Object new_cb) { _on_scan_update->set(new_cb); }
122
+
123
+ void Adapter::on_scan_find(Object new_cb) { _on_scan_find->set(new_cb); }
124
+
125
+ std::unordered_set<std::string> Adapter::status_flags() const {
126
+ using PossibleFlagMap = std::unordered_map<std::string, std::function<bool(const Adapter&)>>;
127
+ static const PossibleFlagMap possible_flag_fns {
128
+ { "initialized"s, &Adapter::initialized },
129
+ { "scanning"s, &Adapter::scan_is_active },
130
+ };
131
+ static auto flag_check = [this](const PossibleFlagMap::value_type &pair) -> bool {
132
+ return pair.second(*this);
133
+ };
134
+
135
+ ranges::viewable_range auto flags = possible_flag_fns | views::filter(flag_check) | views::keys;
136
+ return { flags.begin(), flags.end() };
137
+ }
138
+
139
+ std::string Adapter::to_s() const {
140
+ std::ostringstream oss;
141
+ String superStr(rb_call_super(0, nullptr));
142
+ if (superStr.test() && superStr.length() > 0) {
143
+ std::string super(superStr.str());
144
+ super.pop_back();
145
+ oss << super;
146
+ } else {
147
+ oss << "#<" << Utils::human_type_name<decltype(*this)>();
148
+ oss << ":" << Utils::to_hex_addr(this);
149
+ }
150
+ oss << " ";
151
+ if (!initialized()) return oss.str() + "uninitialized>";
152
+ oss << "@address=\"" << address() << "\" ";
153
+ oss << Utils::join(status_flags(), "|");
154
+ oss << ">";
155
+ return oss.str();
156
+ }
157
+
158
+ void Adapter::ruby_mark() const {
159
+ // std::cout << "ruby_mark-ing adapter proxy: " << address() << std::endl;
160
+ // rb_gc_mark(self());
161
+ Rice::ruby_mark<RuBLE::PeripheralRegistry>(_peripheral_registry.get());
162
+ // rb_gc_mark(_peripheral_registry->self());
163
+ if (_on_scan_start) Rice::ruby_mark<RuBLE::Callback>(_on_scan_start.get());
164
+ if (_on_scan_stop) Rice::ruby_mark<RuBLE::Callback>(_on_scan_stop.get());
165
+ if (_on_scan_update) Rice::ruby_mark<RuBLE::Callback>(_on_scan_update.get());
166
+ if (_on_scan_find) Rice::ruby_mark<RuBLE::Callback>(_on_scan_find.get());
167
+ }
168
+
169
+ void Init_Adapter() {
170
+ define_class_under<SimpleBLE::Adapter>(rb_mRuBLEUnderlying, "SimpleBLEAdapter");
171
+ rb_cAdapter = define_class_under<Adapter>(rb_mRuBLE, "Adapter")
172
+ .define_singleton_function("bluetooth_enabled?", &Adapter::bluetooth_enabled) // returns bool
173
+ .define_singleton_function("get_adapters", &Adapter::get_ruby_adapters) // returns vector<Adapter>
174
+ .define_method("initialized?", &Adapter::initialized)
175
+ .define_method("identifier", &Adapter::identifier)
176
+ .define_method("address", &Adapter::address) // returns BluetoothAddress (alias of std::string)
177
+ .define_method("scan_start", &Adapter::scan_start)
178
+ .define_method("scan_stop", &Adapter::scan_stop)
179
+ .define_method("scan_for", &Adapter::scan_for) // takes (int timeout_ms)
180
+ .define_method("scanning?", &Adapter::scan_is_active)
181
+ .define_method("scan_get_results", &Adapter::scan_get_ruby_results) // returns vector<Peripheral>
182
+ .define_method("get_paired_peripherals",
183
+ &Adapter::get_ruby_paired_peripherals) // returns vector<Peripheral>
184
+ .define_method("on_scan_start", &Adapter::on_scan_start, Arg("cb").keepAlive() = Qnil)
185
+ .define_method("on_scan_stop", &Adapter::on_scan_stop, Arg("cb").keepAlive() = Qnil)
186
+ .define_method("on_scan_update", &Adapter::on_scan_update, Arg("cb").keepAlive() = Qnil)
187
+ .define_method("on_scan_find", &Adapter::on_scan_find, Arg("cb").keepAlive() = Qnil)
188
+ ;
189
+ // define_class_under<std::shared_ptr<RuBLE::Adapter>>(rb_mRuBLE, "AdapterPtr");
190
+ }
191
+ }
192
+
193
+
@@ -0,0 +1,85 @@
1
+ #pragma clang diagnostic push
2
+ #pragma ide diagnostic ignored "modernize-use-nodiscard"
3
+ #pragma once
4
+
5
+ #include "types/declarations.hpp"
6
+ #include "containers/Callback.hpp"
7
+ #include "management/Registry.hpp"
8
+
9
+ #include <unordered_set>
10
+ #include "utils/hash.hpp"
11
+
12
+ namespace RuBLE {
13
+ namespace Rice = ::Rice;
14
+
15
+ // template<Identifiers::OwnerTypedResource T>
16
+ // class ResourceUniqueIdentifier;
17
+
18
+ class Adapter {
19
+ public:
20
+ using Owner = nullptr_t;
21
+ using DataObject [[maybe_unused]] = Data_Object<Adapter>;
22
+
23
+ protected:
24
+ VALUE _self;
25
+ std::shared_ptr<SimpleBLE::Adapter> _adapter;
26
+ BluetoothAddress _addr;
27
+ std::shared_ptr<Callback> _on_scan_start;
28
+ std::shared_ptr<Callback> _on_scan_stop;
29
+ std::shared_ptr<Callback> _on_scan_update;
30
+ std::shared_ptr<Callback> _on_scan_find;
31
+ std::shared_ptr<PeripheralRegistry> _peripheral_registry;
32
+
33
+ public:
34
+ Adapter() = delete;
35
+ Adapter(const SimpleBLE::Adapter &adapter);
36
+ ~Adapter() = default;
37
+
38
+ Object self() const;
39
+
40
+ SimpleBLE::Adapter &get();
41
+ const SimpleBLE::Adapter &get() const;
42
+
43
+ static bool bluetooth_enabled();
44
+ static std::vector<std::shared_ptr<Adapter>> get_adapters();
45
+ static Rice::Array get_ruby_adapters();
46
+ virtual bool initialized() const;
47
+ std::string identifier();
48
+ SimpleBLE::BluetoothAddress address() const;
49
+
50
+ void scan_start();
51
+ void scan_stop();
52
+ void scan_for(int timeout_ms);
53
+ bool scan_is_active() const;
54
+
55
+ void on_scan_start(Object on_scan_start);
56
+ void on_scan_stop(Object on_scan_stop);
57
+ void on_scan_update(Object on_scan_updated);
58
+ void on_scan_find(Object on_scan_found);
59
+
60
+ std::vector<std::shared_ptr<Peripheral>> scan_get_results();
61
+ std::vector<std::shared_ptr<Peripheral>> get_paired_peripherals();
62
+ Rice::Array scan_get_ruby_results();
63
+ Rice::Array get_ruby_paired_peripherals();
64
+ bool operator==(const SimpleBLE::BluetoothAddress &addr) const;
65
+ bool operator==(const Adapter &ap) const;
66
+
67
+ std::unordered_set<std::string> status_flags() const;
68
+ std::string to_s() const;
69
+
70
+ // constexpr ResourceUniqueIdentifier<Adapter> full_resource_identifier() const;
71
+ // constexpr BluetoothAddress this_resource_identifier() const;
72
+
73
+ void ruby_mark() const;
74
+
75
+ template<typename Key, class ProxyClass, class Value>
76
+ friend class Registry;
77
+
78
+ friend void Init_Adapter();
79
+ friend void Init_Registries();
80
+ friend void DeInit(VALUE);
81
+ };
82
+
83
+ }
84
+
85
+ #pragma clang diagnostic pop