Skip to content

Commit

Permalink
central rsutils::json::load_[app_]settings() and ::patch()
Browse files Browse the repository at this point in the history
  • Loading branch information
maloel committed Nov 26, 2023
1 parent 6cec1da commit 2361208
Show file tree
Hide file tree
Showing 4 changed files with 151 additions and 93 deletions.
63 changes: 18 additions & 45 deletions src/context.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,60 +26,33 @@ using json = nlohmann::json;
namespace librealsense {


static nlohmann::json load_config()
{
std::ifstream f( rsutils::os::get_special_folder( rsutils::os::special_folder::app_data ) + RS2_CONFIG_FILENAME );
if( ! f.good() )
return nlohmann::json::object();

// Load the realsense configuration file settings
try
{
return nlohmann::json::parse( f );
}
catch( std::exception const & e )
{
throw invalid_value_exception( "failed to load configuration file: " + std::string( e.what() ) );
}
}


static void merge_settings( nlohmann::json & settings, nlohmann::json const & overrides, std::string const & name )
{
if( ! overrides.is_object() )
throw invalid_value_exception( name + ": expecting an object; got " + overrides.dump() );
try
{
settings.merge_patch( overrides );
}
catch( std::exception const & e )
{
throw invalid_value_exception( "failed to merge " + name + ": " + std::string( e.what() ) );
}
}


static nlohmann::json load_settings( nlohmann::json const & context_settings )
{
// Allow ignoring of any other settings, global or not!
if( ! rsutils::json::get( context_settings, "inherit", true ) )
return context_settings;

auto config = load_config();
nlohmann::json config;

// Take the global 'context' settings out of the configuration
nlohmann::json settings;
if( auto global_context = rsutils::json::nested( config, "context" ) )
merge_settings( settings, global_context, "global config-file/context" );
// Load the realsense configuration file settings
std::ifstream f( rsutils::os::get_special_folder( rsutils::os::special_folder::app_data ) + RS2_CONFIG_FILENAME );
if( f.good() )
{
try
{
config = nlohmann::json::parse( f );
}
catch( std::exception const & e )
{
throw std::runtime_error( "failed to load configuration file: " + std::string( e.what() ) );
}
}

// Merge any application-specific context settings
auto const executable = rsutils::os::executable_name();
if( auto executable_context = rsutils::json::nested( config, executable, "context" ) )
merge_settings( settings, executable_context, "config-file/" + executable + "/context" );
config = rsutils::json::load_settings( config, "context", "config-file" );

// Finally, merge the given context settings on top of all that
merge_settings( settings, context_settings, "context settings" );
return settings;
// Patch the given context settings into the configuration
rsutils::json::patch( config, context_settings, "context settings" );
return config;
}


Expand Down
64 changes: 63 additions & 1 deletion third-party/rsutils/include/rsutils/json.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
// License: Apache 2.0. See LICENSE file in root directory.
// Copyright(c) 2022 Intel Corporation. All Rights Reserved.

#pragma once

#include <nlohmann/json.hpp>
Expand Down Expand Up @@ -173,5 +172,68 @@ class nested
};


// Recursively patches existing 'j' with contents of 'patches', which must be a JSON object.
// A 'null' value inside erases previous contents. Any other value overrides.
// See: https://json.nlohmann.me/api/basic_json/merge_patch/
// Example below, for load_app_settings.
// Use 'what' to denote what it is we're patching in, if a failure happens. The std::runtime_error will populate with
// it.
//
void patch( nlohmann::json & j, nlohmann::json const & patches, std::string const & what = {} );


// Loads configuration settings from 'global' content.
// E.g., a configuration file may contain:
// {
// "context": {
// "dds": {
// "enabled": false,
// "domain" : 5
// }
// },
// ...
// }
// This function will load a specific key 'context' inside and return it. The result will be a disabling of dds:
// Besides this "global" key, application-specific settings can override the global settings, e.g.:
// {
// "context": {
// "dds": {
// "enabled": false,
// "domain" : 5
// }
// },
// "realsense-viewer": {
// "context": {
// "dds": { "enabled": null }
// }
// },
// ...
// }
// If the current application is 'realsense-viewer', then the global 'context' settings will be patched with the
// application-specific 'context' and returned:
// {
// "dds": {
// "domain" : 5
// }
// }
// See rules for patching in patch().
// The 'application' is usually any single-word executable name (without extension).
// The 'subkey' is mandatory.
// The 'error_context' is used for error reporting, to show what failed. Like application, it should be a single word
// that can be used to denote hierarchy within the global json.
//
nlohmann::json load_app_settings( nlohmann::json const & global,
std::string const & application,
std::string const & subkey,
std::string const & error_context );


// Same as above, but automatically takes the application name from the executable-name.
//
nlohmann::json load_settings( nlohmann::json const & global,
std::string const & subkey,
std::string const & error_context );


} // namespace json
} // namespace rsutils
46 changes: 46 additions & 0 deletions third-party/rsutils/src/json.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Copyright(c) 2023 Intel Corporation. All Rights Reserved.

#include <rsutils/json.h>
#include <rsutils/os/executable-name.h>

namespace rsutils {
namespace json {
Expand All @@ -10,5 +11,50 @@ namespace json {
nlohmann::json const null_json = {};


void patch( nlohmann::json & j, nlohmann::json const & patches, std::string const & what )
{
if( ! patches.is_object() )
{
std::string context = what.empty() ? std::string( "patch", 5 ) : what;
throw std::runtime_error( context + ": expecting an object; got " + patches.dump() );
}

try
{
j.merge_patch( patches );
}
catch( std::exception const & e )
{
std::string context = what.empty() ? std::string( "patch", 5 ) : what;
throw std::runtime_error( "failed to merge " + context + ": " + e.what() );
}
}


nlohmann::json load_app_settings( nlohmann::json const & global,
std::string const & application,
std::string const & subkey,
std::string const & error_context )
{
// Take the global subkey settings out of the configuration
nlohmann::json settings;
if( auto global_subkey = rsutils::json::nested( global, subkey ) )
patch( settings, global_subkey, "global " + error_context + '/' + subkey );

// Patch any application-specific subkey settings
if( auto application_subkey = rsutils::json::nested( global, application, subkey ) )
patch( settings, application_subkey, error_context + '/' + application + '/' + subkey );

return settings;
}


nlohmann::json
load_settings( nlohmann::json const & global, std::string const & subkey, std::string const & error_context )
{
return load_app_settings( global, rsutils::os::executable_name(), subkey, error_context );
}


} // namespace json
} // namespace rsutils
71 changes: 24 additions & 47 deletions tools/dds/dds-adapter/rs-dds-adapter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -60,60 +60,33 @@ topics::device_info rs2_device_to_info( rs2::device const & dev )
}


static nlohmann::json load_config()
static nlohmann::json load_settings( nlohmann::json const & local_settings )
{
std::ifstream f( rsutils::os::get_special_folder( rsutils::os::special_folder::app_data ) + RS2_CONFIG_FILENAME );
if( ! f.good() )
return nlohmann::json::object();
nlohmann::json config;

// Load the realsense configuration file settings
try
{
return nlohmann::json::parse( f );
}
catch( std::exception const & e )
{
throw std::runtime_error( "failed to load configuration file: " + std::string( e.what() ) );
}
}


static void merge_settings( nlohmann::json & settings, nlohmann::json const & overrides, std::string const & name )
{
if( ! overrides.is_object() )
throw std::runtime_error( name + ": expecting an object; got " + overrides.dump() );
try
{
settings.merge_patch( overrides );
}
catch( std::exception const & e )
std::ifstream f( rsutils::os::get_special_folder( rsutils::os::special_folder::app_data ) + RS2_CONFIG_FILENAME );
if( f.good() )
{
throw std::runtime_error( "failed to merge " + name + ": " + std::string( e.what() ) );
try
{
config = nlohmann::json::parse( f );
}
catch( std::exception const & e )
{
throw std::runtime_error( "failed to load configuration file: " + std::string( e.what() ) );
}
}
}


static nlohmann::json load_settings( nlohmann::json const & local_settings )
{
// Allow ignoring of any other settings, global or not!
auto config = load_config();

// Take the global 'context' settings out of the configuration
nlohmann::json settings;
if( auto global_context = rsutils::json::nested( config, "context" ) )
merge_settings( settings, global_context, "global config-file/context" );

// Merge any application-specific context settings
auto const executable = rsutils::os::executable_name();
if( auto executable_context = rsutils::json::nested( config, executable, "context" ) )
merge_settings( settings, executable_context, "config-file/" + executable + "/context" );
config = rsutils::json::load_settings( config, "context", "config-file" );

// Take the "dds" settings only
settings = rsutils::json::nested( settings, "dds" );
config = rsutils::json::nested( config, "dds" );

// Finally, merge the given local settings on top of all that
merge_settings( settings, local_settings, "local settings" );
return settings;
// Patch the given local settings into the configuration
rsutils::json::patch( config, local_settings, "local settings" );

return config;
}


Expand Down Expand Up @@ -165,8 +138,12 @@ try

// Create a DDS participant
auto participant = std::make_shared< dds_participant >();
auto settings = load_settings( nlohmann::json::object() );
participant->init( domain, "rs-dds-adapter", std::move( settings ) );
{
nlohmann::json dds_settings;
dds_settings["enabled"]; // create a 'null' json key in there to remove any enabled:false
dds_settings = load_settings( dds_settings );
participant->init( domain, "rs-dds-adapter", std::move( dds_settings ) );
}

struct device_handler
{
Expand Down

0 comments on commit 2361208

Please sign in to comment.