|
7 | 7 | #ifndef LIBREALSENSE_RS2_COMPOSITE_OPTION_HPP |
8 | 8 | #define LIBREALSENSE_RS2_COMPOSITE_OPTION_HPP |
9 | 9 |
|
10 | | -#include "rs_sensor.hpp" |
| 10 | +#include "rs_options.hpp" |
11 | 11 | #include "../h/rs_composite_option.h" |
12 | 12 |
|
| 13 | +#include <memory> |
| 14 | +#include <vector> |
| 15 | +#include <cstdint> |
| 16 | + |
13 | 17 | namespace rs2 |
14 | 18 | { |
15 | | - // Generic "composite option" extension: any sensor that supports AT LEAST ONE composite |
16 | | - // (multi-field, atomically-exchanged) option extends to this - see is<T>()/as<T>() in |
17 | | - // rs_types.hpp, backed by rs2_is_sensor_extendable_to(RS2_EXTENSION_COMPOSITE_OPTIONS). |
18 | | - // There is one wrapper class for every composite option, not one per feature: which option |
19 | | - // you are getting/setting is selected by the rs2_composite_option_id argument. |
20 | | - class composite_option_sensor : public sensor |
| 19 | + // Lightweight, non-owning reference to a single (options-object, rs2_option id) pair - |
| 20 | + // obtained via options::option_ref(). Mirrors rs2::frame's is<T>()/as<T>() dispatch |
| 21 | + // (rs_frame.hpp / rs2_is_frame_extendable_to) exactly, just for options instead of frames: |
| 22 | + // is<T>() / as<T>() construct a T from *this and rely on T's converting constructor to flag |
| 23 | + // validity (see composite_option below), rather than throwing. |
| 24 | + // |
| 25 | + // Does NOT own the underlying rs2_options* - it shares the lifetime of whichever |
| 26 | + // sensor/embedded_filter produced it via options::option_ref(); the caller must keep that |
| 27 | + // object alive for as long as this handle (or anything cast from it) is used. |
| 28 | + class option_handle |
| 29 | + { |
| 30 | + public: |
| 31 | + option_handle() = default; |
| 32 | + |
| 33 | + option_handle( std::shared_ptr< rs2_options > options, rs2_option id ) |
| 34 | + : _options( std::move( options ) ) |
| 35 | + , _id( id ) |
| 36 | + { |
| 37 | + } |
| 38 | + |
| 39 | + template< class T > |
| 40 | + bool is() const |
| 41 | + { |
| 42 | + T extension( *this ); |
| 43 | + return extension; |
| 44 | + } |
| 45 | + |
| 46 | + template< class T > |
| 47 | + T as() const |
| 48 | + { |
| 49 | + T extension( *this ); |
| 50 | + return extension; |
| 51 | + } |
| 52 | + |
| 53 | + rs2_option get_option_id() const { return _id; } |
| 54 | + |
| 55 | + operator bool() const { return _valid && _options != nullptr; } |
| 56 | + |
| 57 | + protected: |
| 58 | + std::shared_ptr< rs2_options > _options; |
| 59 | + rs2_option _id = RS2_OPTION_COUNT; |
| 60 | + bool _valid = true; |
| 61 | + }; |
| 62 | + |
| 63 | + // Generic "composite option" extension: any option that is a multi-field, atomically |
| 64 | + // exchanged composite control (see rs_composite_option.h) extends to this - one wrapper |
| 65 | + // class shared by every such option, not one per feature. Which option this refers to is |
| 66 | + // carried by the underlying option_handle's id - obtain one via options::option_ref(id). |
| 67 | + class composite_option : public option_handle |
21 | 68 | { |
22 | 69 | public: |
23 | | - composite_option_sensor(sensor s) |
24 | | - : sensor(s.get()) |
| 70 | + // Mirrors rs2::video_frame(const rs2::frame&) exactly (see rs_frame.hpp): checks the |
| 71 | + // extension via the C API and marks itself invalid (never throws) on mismatch. Guarded |
| 72 | + // by rs2_supports_option() first (a plain, never-throwing lookup) because - unlike |
| 73 | + // rs2_is_sensor_extendable_to/rs2_is_frame_extendable_to, which only ever risk a |
| 74 | + // dynamic_cast - rs2_is_option_extendable_to looks up the option by id first, which |
| 75 | + // throws (surfaced as an rs2_error) if this id isn't registered on the container at all; |
| 76 | + // that's a legitimate "not this option" case here, not an error to propagate. |
| 77 | + composite_option( const option_handle & h ) |
| 78 | + : option_handle( h ) |
25 | 79 | { |
26 | | - rs2_error* e = nullptr; |
27 | | - if (rs2_is_sensor_extendable_to(_sensor.get(), RS2_EXTENSION_COMPOSITE_OPTIONS, &e) == 0 && !e) |
| 80 | + rs2_error * e = nullptr; |
| 81 | + bool extendable = _options && rs2_supports_option( _options.get(), _id, &e ) > 0; |
| 82 | + error::handle( e ); |
| 83 | + if( extendable ) |
28 | 84 | { |
29 | | - _sensor.reset(); |
| 85 | + extendable = rs2_is_option_extendable_to( _options.get(), _id, RS2_EXTENSION_COMPOSITE_OPTION, &e ) > 0; |
| 86 | + error::handle( e ); |
30 | 87 | } |
31 | | - error::handle(e); |
| 88 | + if( ! extendable ) |
| 89 | + _valid = false; |
32 | 90 | } |
33 | 91 |
|
34 | | - operator bool() const { return _sensor.get() != nullptr; } |
35 | | - |
36 | | - // Single atomic UVC transaction (one set_xu round trip) - all fields of *data sent together. |
37 | | - void set_composite_option(rs2_composite_option_id option_id, const void* data, unsigned int data_size) const |
| 92 | + // Single atomic UVC transaction (one set_xu round trip) - all fields of *data sent |
| 93 | + // together. No ownership transfer - data remains caller-owned, like options::set_option. |
| 94 | + void set( const void * data, size_t size ) const |
38 | 95 | { |
39 | | - rs2_error* e = nullptr; |
40 | | - rs2_set_composite_option(_sensor.get(), option_id, data, data_size, &e); |
41 | | - error::handle(e); |
| 96 | + rs2_error * e = nullptr; |
| 97 | + rs2_set_composite_option( _options.get(), _id, data, static_cast< unsigned int >( size ), &e ); |
| 98 | + error::handle( e ); |
42 | 99 | } |
43 | 100 |
|
44 | 101 | // Single atomic UVC transaction (one get_xu round trip). The SDK allocates the result |
45 | | - // (the caller has no generic way to know option_id's wire size in advance) and this |
46 | | - // wrapper hides the raw rs2_raw_data_buffer/manual-free entirely, returning a plain |
47 | | - // std::vector<uint8_t> of the option's raw payload bytes. The SDK ships no typed |
48 | | - // struct for this - cast/memcpy the bytes into your own local struct matching the |
49 | | - // layout documented on the corresponding rs2_composite_option_id enumerator (see |
50 | | - // rs_composite_option.h). |
51 | | - std::vector<uint8_t> get_composite_option(rs2_composite_option_id option_id) const |
| 102 | + // (the caller has no generic way to know this option's wire size in advance) and this |
| 103 | + // wrapper hides the raw rs2_raw_data_buffer/manual-free entirely (mirrors |
| 104 | + // rs2::safety_sensor::get_safety_preset's exact unwrap pattern), returning a plain |
| 105 | + // std::vector<uint8_t> of the option's current raw payload bytes. |
| 106 | + std::vector< uint8_t > get() const |
| 107 | + { |
| 108 | + rs2_error * e = nullptr; |
| 109 | + auto buffer = rs2_get_composite_option( _options.get(), _id, &e ); |
| 110 | + return unwrap( buffer, e ); |
| 111 | + } |
| 112 | + |
| 113 | + // Same unwrap pattern as get(), for the option's supported {min,max,step,def} - see |
| 114 | + // rs_hkr_temporal_filter_dpp.h for RS2_OPTION_HKR_TEMPORAL_FILTER_DPP's range struct. |
| 115 | + std::vector< uint8_t > get_range() const |
| 116 | + { |
| 117 | + rs2_error * e = nullptr; |
| 118 | + auto buffer = rs2_get_composite_option_range( _options.get(), _id, &e ); |
| 119 | + return unwrap( buffer, e ); |
| 120 | + } |
| 121 | + |
| 122 | + // Full functional parity with plain rs2::option-style metadata queries - pure |
| 123 | + // metadata/no value-marshaling, so these just delegate through normally. |
| 124 | + bool is_read_only() const |
| 125 | + { |
| 126 | + rs2_error * e = nullptr; |
| 127 | + auto res = rs2_is_option_read_only( _options.get(), _id, &e ); |
| 128 | + error::handle( e ); |
| 129 | + return res > 0; |
| 130 | + } |
| 131 | + |
| 132 | + // Reflects real, current runtime availability (delegates to rs2_supports_option) - this |
| 133 | + // is NOT the mechanism used to block scalar rs2_get_option/rs2_set_option access on a |
| 134 | + // composite option (that's a hard-blocked, always-throws operation on the underlying |
| 135 | + // option implementation - see librealsense::composite_xu_option). |
| 136 | + bool is_enabled() const |
52 | 137 | { |
53 | | - rs2_error* e = nullptr; |
54 | | - auto buffer = rs2_get_composite_option(_sensor.get(), option_id, &e); |
| 138 | + rs2_error * e = nullptr; |
| 139 | + auto res = rs2_supports_option( _options.get(), _id, &e ); |
| 140 | + error::handle( e ); |
| 141 | + return res > 0; |
| 142 | + } |
55 | 143 |
|
56 | | - std::shared_ptr<const rs2_raw_data_buffer> list(buffer, rs2_delete_raw_data); |
57 | | - error::handle(e); |
| 144 | + const char * get_description() const |
| 145 | + { |
| 146 | + rs2_error * e = nullptr; |
| 147 | + auto res = rs2_get_option_description( _options.get(), _id, &e ); |
| 148 | + error::handle( e ); |
| 149 | + return res; |
| 150 | + } |
58 | 151 |
|
59 | | - auto size = rs2_get_raw_data_size(list.get(), &e); |
60 | | - error::handle(e); |
| 152 | + private: |
| 153 | + static std::vector< uint8_t > unwrap( const rs2_raw_data_buffer * buffer, rs2_error * e ) |
| 154 | + { |
| 155 | + std::shared_ptr< const rs2_raw_data_buffer > list( buffer, rs2_delete_raw_data ); |
| 156 | + error::handle( e ); |
61 | 157 |
|
62 | | - auto start = rs2_get_raw_data(list.get(), &e); |
63 | | - error::handle(e); |
| 158 | + rs2_error * e2 = nullptr; |
| 159 | + auto size = rs2_get_raw_data_size( list.get(), &e2 ); |
| 160 | + error::handle( e2 ); |
64 | 161 |
|
65 | | - std::vector<uint8_t> result; |
66 | | - result.insert(result.begin(), start, start + size); |
| 162 | + auto start = rs2_get_raw_data( list.get(), &e2 ); |
| 163 | + error::handle( e2 ); |
67 | 164 |
|
| 165 | + std::vector< uint8_t > result; |
| 166 | + result.insert( result.begin(), start, start + size ); |
68 | 167 | return result; |
69 | 168 | } |
70 | 169 | }; |
| 170 | + |
| 171 | + inline option_handle options::option_ref( rs2_option id ) const |
| 172 | + { |
| 173 | + // Non-owning: no-op deleter. The real object (sensor/embedded_filter) that _options |
| 174 | + // aliases owns its own lifetime elsewhere - this handle just shares it, exactly as |
| 175 | + // rs2::options itself only ever holds a raw, non-owning rs2_options* alias (see |
| 176 | + // options::_options / options(rs2_options*) above). |
| 177 | + return option_handle( std::shared_ptr< rs2_options >( _options, []( rs2_options * ) {} ), id ); |
| 178 | + } |
71 | 179 | } |
72 | 180 | #endif // LIBREALSENSE_RS2_COMPOSITE_OPTION_HPP |
0 commit comments