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