Skip to content

Commit

Permalink
PR #13074 from Eran: viewer presets for dds devices
Browse files Browse the repository at this point in the history
  • Loading branch information
maloel authored Jun 27, 2024
2 parents d1379b3 + d7cb155 commit a203640
Show file tree
Hide file tree
Showing 10 changed files with 351 additions and 18 deletions.
7 changes: 6 additions & 1 deletion common/device-model.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1865,7 +1865,12 @@ namespace rs2
auto itr = sub->options_metadata.find(RS2_OPTION_VISUAL_PRESET);
if (itr != sub->options_metadata.end())
{
itr->second.endpoint->set_option(RS2_OPTION_VISUAL_PRESET, RS2_RS400_VISUAL_PRESET_CUSTOM);
// Make sure we actually have a "Custom" preset
if( std::string( "Custom", 6 )
== itr->second.endpoint->get_option_value_description(
RS2_OPTION_VISUAL_PRESET,
RS2_RS400_VISUAL_PRESET_CUSTOM ) )
itr->second.endpoint->set_option(RS2_OPTION_VISUAL_PRESET, RS2_RS400_VISUAL_PRESET_CUSTOM);
}
}
}
Expand Down
20 changes: 20 additions & 0 deletions src/dds/rs-dds-device-proxy.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -696,4 +696,24 @@ void dds_device_proxy::update( const void * /*image*/, int /*image_size*/, rs2_u
}


std::vector< sensor_interface * > dds_device_proxy::get_serializable_sensors()
{
std::vector< sensor_interface * > sensors;
auto const n_sensors = get_sensors_count();
for( auto i = 0; i < n_sensors; ++i )
sensors.push_back( &get_sensor( i ) );
return sensors;
}


std::vector< sensor_interface const * > dds_device_proxy::get_serializable_sensors() const
{
std::vector< sensor_interface const * > sensors;
auto const n_sensors = get_sensors_count();
for( auto i = 0; i < n_sensors; ++i )
sensors.push_back( &get_sensor( i ) );
return sensors;
}


} // namespace librealsense
7 changes: 7 additions & 0 deletions src/dds/rs-dds-device-proxy.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include <src/fw-update/fw-update-device-interface.h>
#include <src/core/debug.h>
#include "sid_index.h"
#include "rsdds-serializable.h"

#include <memory>
#include <vector>
Expand Down Expand Up @@ -40,6 +41,7 @@ class dds_device_proxy
, public debug_interface
, public updatable // unsigned, non-recovery-mode
, public update_device_interface // signed, recovery-mode
, public dds_serializable
{
std::shared_ptr< realdds::dds_device > _dds_dev;
std::map< std::string, std::vector< std::shared_ptr< stream_profile_interface > > > _stream_name_to_profiles;
Expand Down Expand Up @@ -90,6 +92,11 @@ class dds_device_proxy
private:
void update( const void * image, int image_size, rs2_update_progress_callback_sptr = nullptr ) const override;

// dds_serializable
private:
device_interface const & get_serializable_device() const override { return *this; }
std::vector< sensor_interface * > get_serializable_sensors() override;
std::vector< sensor_interface const * > get_serializable_sensors() const override;
};


Expand Down
6 changes: 6 additions & 0 deletions src/dds/rs-dds-option.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,9 @@ void rs_dds_option::set( float value )
<< "use rs2_set_option_value to set " << get_string( _rs_type ) << " value" );
}

if( is_read_only() )
throw invalid_value_exception( "option is read-only: " + _dds_opt->get_name() );

_set_opt_cb( j_value );
}

Expand Down Expand Up @@ -158,6 +161,9 @@ void rs_dds_option::set_value( json value )
if( ! _set_opt_cb )
throw std::runtime_error( "Set option callback is not set for option " + _dds_opt->get_name() );

if( is_read_only() )
throw invalid_value_exception( "option is read-only: " + _dds_opt->get_name() );

_set_opt_cb( std::move( value ) );
}

Expand Down
170 changes: 170 additions & 0 deletions src/dds/rsdds-serializable.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
// License: Apache 2.0. See LICENSE file in root directory.
// Copyright(c) 2024 Intel Corporation. All Rights Reserved.

#include "rsdds-serializable.h"

#include <src/serialized-utilities.h>
#include <src/core/sensor-interface.h>
#include <src/core/device-interface.h>

#include <rsutils/json.h>
#include <set>

using json = nlohmann::json;


static std::set< rs2_option > get_options_to_ignore()
{
return {
RS2_OPTION_FRAMES_QUEUE_SIZE, // Internally added and is not an option we need to record/load
RS2_OPTION_REGION_OF_INTEREST // The RoI is temporary, uses another mechanism for get/set, and we don't load it
};
}


namespace librealsense {


std::vector< uint8_t > dds_serializable::serialize_json() const
{
serialized_utilities::json_preset_writer writer;
writer.set_device_info( get_serializable_device() );

// Set of options that should not be written
auto const options_to_ignore = get_options_to_ignore();

for( auto p_sensor : get_serializable_sensors() )
{
std::string const sensor_name = p_sensor->get_info( RS2_CAMERA_INFO_NAME );
for( auto o : p_sensor->get_supported_options() )
{
auto & opt = p_sensor->get_option( o );
if( opt.is_read_only() )
continue;
if( options_to_ignore.find( o ) != options_to_ignore.end() )
continue;

auto val = opt.get_value();
writer.write_param( sensor_name + '/' + get_string( o ), val );
}
}

auto str = writer.to_string();
return std::vector< uint8_t >( str.begin(), str.end() );
}


void dds_serializable::load_json( const std::string & json_content )
{
serialized_utilities::json_preset_reader reader( json_content );

// Verify if device information in preset file is compatible with the connected device.
reader.check_device_info( get_serializable_device() );
auto const sensors = get_serializable_sensors();

// Do the visual preset first, as it sets the basis on top of which we apply the regular controls
{
auto const visual_preset_subkey = '/' + get_string( RS2_OPTION_VISUAL_PRESET );
for( auto const p_sensor : sensors )
{
std::string const sensor_name = p_sensor->get_info( RS2_CAMERA_INFO_NAME );
auto const key = sensor_name + visual_preset_subkey;
auto it = reader.find( key );
if( it == reader.end() )
continue;

try
{
auto & opt = p_sensor->get_option( RS2_OPTION_VISUAL_PRESET );
opt.set_value( it.value() );
LOG_DEBUG( "loaded '" << key << "' value " << it.value() );
}
catch( std::exception const & e )
{
LOG_ERROR( "Failed to load '" << key << "' (value " << it.value() << "): " << e.what() );
throw;
}
}
}

// Create a set of options we have to change
std::map< std::string /*key*/, std::string /*error*/ > keys_to_change;
{
// Set of options that should not be set in the loop
auto options_to_ignore = get_options_to_ignore();
options_to_ignore.insert( RS2_OPTION_VISUAL_PRESET ); // We don't want to do this again

for( auto const p_sensor : sensors )
{
std::string const sensor_name = p_sensor->get_info( RS2_CAMERA_INFO_NAME );
for( auto option_id : p_sensor->get_supported_options() )
{
if( options_to_ignore.find( option_id ) != options_to_ignore.end() )
continue;
auto key = sensor_name + '/' + get_string( option_id );
if( reader.find( key ) == reader.end() )
continue;

keys_to_change.emplace( std::move( key ), std::string() );
}
}
}

// Some options may depend on others being set first (e.g., they are read-only until another option is set); these
// will generate errors. Since we don't know which order to set options in, we track failures and keep trying as
// long as at least one option value is changed:
while( true )
{
std::map< std::string /*key*/, std::string /*error*/ > keys_left;
for( auto const p_sensor : sensors )
{
std::string const sensor_name = p_sensor->get_info( RS2_CAMERA_INFO_NAME );
for( auto option_id : p_sensor->get_supported_options() )
{
auto key = sensor_name + '/' + get_string( option_id );
if( keys_to_change.find( key ) == keys_to_change.end() )
continue;

auto it = reader.find( key );
if( it != reader.end() )
{
try
{
auto & opt = p_sensor->get_option( option_id );
opt.set_value( it.value() );
LOG_DEBUG( "loaded '" << key << "' value " << it.value() );
continue;
}
catch( unrecoverable_exception const & )
{
throw;
}
catch( std::exception const & e )
{
std::string error = e.what();
LOG_DEBUG( "Failed to load '" << key << "' (value " << it.value() << "): " << error );
keys_left.emplace( std::move( key ), std::move( error ) );
}
}
}
}
if( keys_left.empty() )
// Nothing more to do; we can stop
break;
if( keys_left.size() == keys_to_change.size() )
{
// We still have failures, but nothing's changed; we must stop
for( auto const & key_error : keys_left )
{
auto it = reader.find( key_error.first );
if( it != reader.end() )
LOG_ERROR( "Failed to load '" << key_error.first << "' (value " << it.value() << "): " << key_error.second );
}
break;
}
keys_to_change = std::move( keys_left );
}
}


} // namespace librealsense
28 changes: 28 additions & 0 deletions src/dds/rsdds-serializable.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// License: Apache 2.0. See LICENSE file in root directory.
// Copyright(c) 2024 Intel Corporation. All Rights Reserved.
#pragma once

#include <src/serializable-interface.h>


namespace librealsense {


class device_interface;
class sensor_interface;


class dds_serializable : public serializable_interface
{
public:
std::vector< uint8_t > serialize_json() const override;
void load_json( const std::string & json_content ) override;

protected:
virtual device_interface const & get_serializable_device() const = 0;
virtual std::vector< sensor_interface * > get_serializable_sensors() = 0;
virtual std::vector< sensor_interface const * > get_serializable_sensors() const = 0;
};


} // namespace librealsense
32 changes: 20 additions & 12 deletions src/serializable-interface.h
Original file line number Diff line number Diff line change
@@ -1,17 +1,25 @@
// License: Apache 2.0. See LICENSE file in root directory.
// Copyright(c) 2020 Intel Corporation. All Rights Reserved.

// Copyright(c) 2020-4 Intel Corporation. All Rights Reserved.
#pragma once
#include "types.h"

#include "core/extension.h"
#include <vector>
#include <string>


namespace librealsense {


namespace librealsense
class serializable_interface
{
class serializable_interface
{
public:
virtual std::vector<uint8_t> serialize_json() const = 0;
virtual void load_json(const std::string& json_content) = 0;
};
MAP_EXTENSION(RS2_EXTENSION_SERIALIZABLE, serializable_interface);
}
public:
virtual ~serializable_interface() {}

virtual std::vector< uint8_t > serialize_json() const = 0;
virtual void load_json( const std::string & json_content ) = 0;
};

MAP_EXTENSION( RS2_EXTENSION_SERIALIZABLE, serializable_interface );


} // namespace librealsense
4 changes: 4 additions & 0 deletions third-party/realdds/py/pyrealdds.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -913,6 +913,10 @@ PYBIND11_MODULE(NAME, m) {
.def( "publish_metadata", &dds_device_server::publish_metadata, py::call_guard< py::gil_scoped_release >() )
.def( "broadcast", &dds_device_server::broadcast )
.def( "broadcast_disconnect", &dds_device_server::broadcast_disconnect, py::arg( "ack-timeout" ) = dds_time() )
.def( FN_FWD( dds_device_server, on_set_option,
(dds_device_server &, std::shared_ptr< realdds::dds_option > const &, json_ref &&),
( std::shared_ptr< realdds::dds_option > const & option, json & value ),
callback( self, option, json_ref{ value } ); ) )
.def( FN_FWD_R( dds_device_server, on_control,
false,
(dds_device_server &, std::string const &, py::object &&, json_ref &&),
Expand Down
Loading

0 comments on commit a203640

Please sign in to comment.