66#####################################################
77
88
9- import struct
109import sys
1110import pyrealsense2 as rs
1211
21205. For each embedded filter: show supported COMPOSITE options and print current values too -
2221 these are a completely separate identity space from the ordinary (scalar) options above (see
2322 rs2_composite_option_id in rs_composite_option.h): a single multi-field control exchanged
24- atomically, in ONE UVC transaction. get/set_composite_option() hand back/take raw bytes rather
25- than a typed value - Python has no templates, so (mirroring the C++/C99 samples under
26- examples/composite-option and examples/C/composite-ctl) the caller unpacks/packs those bytes
27- against the option's documented wire layout with the `struct` module.
23+ atomically, in ONE UVC transaction. Each id known to this example is registered below against
24+ its typed accessor method (e.g. get_improved_close_range_control()) - the Python equivalent of
25+ the C++ wrapper's get_composite_option_as<T>() template call (see
26+ wrappers/python/pyrs_options.cpp) - so what comes back is a real object bound field-for-field
27+ against the actual C struct, not raw bytes. Fields are then read off that object by
28+ introspection, so this example carries no hand-maintained wire layout of its own: no struct
29+ format string, no byte offsets, no field list to keep in sync by hand.
2830'''
2931
3032def list_embedded_filter_options (embedded_filter ):
@@ -35,25 +37,27 @@ def list_embedded_filter_options(embedded_filter):
3537 print ("\n " )
3638
3739
38- # Known wire layouts for composite options this example knows how to interpret - there is no
39- # generic "any composite option" cast (the SDK ships no per-id dispatch), so a new composite
40- # option needs its layout added here too, same as every other composite-option sample's typed
41- # dispatch. See rs_hkr_improved_close_range_control.h / rs_hkr_temporal_filter_dpp.h for the documented layouts.
42- _COMPOSITE_OPTION_LAYOUTS = {
43- rs .composite_option_id .hkr_improved_close_range_control : (
44- # dppc_header (version, flags, ctl_id, param_count, param_type) shared by the whole HKR
45- # DPP control family, then Improved Close Range's 7 logical fields, then 1 reserved (always 0) slot.
46- '<BBHBBiiiiiiii' ,
47- ['version' , 'flags' , 'ctl_id' , 'param_count' , 'param_type' ,
48- 'enable' , 'filter_type' , 'downscale_ratio' , 'shift_mode' , 'shift_pixels' ,
49- 'threshold_mode' , 'threshold_mm' , 'reserved0' ]),
50- rs .composite_option_id .hkr_temporal_filter_dpp : (
51- # No shared header on this one - just its own 4 fields, tightly packed.
52- '<ifii' ,
53- ['enabled' , 'smooth_alpha' , 'smooth_delta' , 'persistency_index' ]),
40+ # Typed accessor method registered per known composite-option id - the SDK ships no generic "any
41+ # composite option" cast (there is no per-id dispatch table, in C++ or Python), so a new composite
42+ # option still needs an entry here, same as every other composite-option sample in this repo
43+ # dispatches by known id. Unlike a raw wire-layout table though, nothing here names a byte offset
44+ # or format string: the registered method returns a real typed object (see
45+ # rs.improved_close_range_control / rs.temporal_filter_dpp_config in pyrs_options.cpp), and
46+ # _typed_fields() below reads its fields back by introspection.
47+ _COMPOSITE_OPTION_ACCESSORS = {
48+ rs .composite_option_id .hkr_improved_close_range_control : 'get_improved_close_range_control' ,
49+ rs .composite_option_id .hkr_temporal_filter_dpp : 'get_temporal_filter_dpp_config' ,
5450}
5551
5652
53+ def _typed_fields (value ):
54+ '''The real, public fields on a typed composite-option object, discovered from the object
55+ itself rather than a hand-maintained name list. `header` is skipped - it's wire-transport
56+ framing (version/ctl_id/param_count/...), not one of the control's own logical fields.'''
57+ return [name for name in dir (value )
58+ if not name .startswith ('_' ) and name != 'header' and not callable (getattr (value , name ))]
59+
60+
5761def list_embedded_filter_composite_options (embedded_filter ):
5862 composite_ids = embedded_filter .get_supported_composite_options ()
5963 if not composite_ids :
@@ -65,8 +69,14 @@ def list_embedded_filter_composite_options(embedded_filter):
6569 print (" {}:" .format (repr (id )))
6670 print (" read-only: {}" .format (embedded_filter .is_composite_option_read_only (id )))
6771 print (" description: \" {}\" " .format (embedded_filter .get_composite_option_description (id )))
72+
73+ accessor_name = _COMPOSITE_OPTION_ACCESSORS .get (id )
74+ if accessor_name is None :
75+ print (" (no typed accessor registered for this id in this example - see "
76+ "get_composite_option()/get_composite_option_range() for the untyped, raw-bytes API)" )
77+ continue
6878 try :
69- raw = embedded_filter . get_composite_option (id )
79+ value = getattr ( embedded_filter , accessor_name ) (id )
7080 except RuntimeError as e :
7181 # Registered but not actually functional on this device/FW is a real, expected
7282 # outcome (supports_composite_option()/get_supported_composite_options() only
@@ -75,15 +85,8 @@ def list_embedded_filter_composite_options(embedded_filter):
7585 print (" SKIPPED (registered but not functional on this device/FW): {}" .format (e ))
7686 continue
7787
78- layout = _COMPOSITE_OPTION_LAYOUTS .get (id )
79- if layout is None :
80- print (" raw bytes ({}): {}" .format (len (raw ), raw .hex ()))
81- continue
82-
83- fmt , field_names = layout
84- values = struct .unpack (fmt , raw )
85- for name , value in zip (field_names , values ):
86- print (" {} = {}" .format (name , value ))
88+ for name in _typed_fields (value ):
89+ print (" {} = {}" .format (name , getattr (value , name )))
8790 print ("\n " )
8891
8992
0 commit comments