Skip to content

Commit

Permalink
Initial implementation of backup and restore
Browse files Browse the repository at this point in the history
  • Loading branch information
jonathan-r-thorpe committed Oct 7, 2024
1 parent 681b897 commit 9e5e604
Show file tree
Hide file tree
Showing 11 changed files with 418 additions and 167 deletions.
154 changes: 131 additions & 23 deletions Development/nmos-cpp-node/node_implementation.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#include "node_implementation.h"

#include <algorithm>
#include <boost/range/adaptor/filtered.hpp>
#include <boost/range/adaptor/transformed.hpp>
#include <boost/range/algorithm/find.hpp>
Expand Down Expand Up @@ -1719,14 +1720,15 @@ nmos::control_protocol_property_changed_handler make_node_implementation_control
};
}

void get_object_property_holder(const nmos::resources& resources, slog::base_gate& gate, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, const nmos::resource& resource, bool recurse, web::json::value& object_properties_holders)
web::json::value make_property_value_holders(const nmos::resource& resource, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor)
{
using web::json::value;

value property_value_holders = value::array();

nmos::nc_class_id class_id = nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(resource.data));

// Get properties for object
while (!class_id.empty())
{
// find the relevant nc_property_descriptor
Expand All @@ -1740,33 +1742,54 @@ void get_object_property_holder(const nmos::resources& resources, slog::base_gat
}
class_id.pop_back();
}
return property_value_holders;
}

web::json::value get_role_path(const nmos::resources& resources, const nmos::resource& resource)
{
// Find role path for object
// Hmmm do we not have a library function to do this?

using web::json::value;

auto role_path = value::array();
web::json::push_back(role_path, nmos::fields::nc::role(resource.data));

auto role_path = nmos::fields::nc::role(resource.data);
auto oid = nmos::fields::nc::id(resource.data);
nmos::resource found_resource = resource;

while (utility::s2us(std::to_string(nmos::root_block_oid)) != oid.as_string())
{
auto owner = nmos::fields::nc::owner(found_resource.data);
const auto& found = nmos::find_resource(resources, utility::s2us(std::to_string(owner)));
const auto& found = nmos::find_resource(resources, utility::s2us(std::to_string(nmos::fields::nc::owner(found_resource.data))));
if (resources.end() == found)
{
break;
}

found_resource = (*found);
role_path = nmos::fields::nc::role(found_resource.data) + U(".") + role_path;
web::json::push_back(role_path, nmos::fields::nc::role(found_resource.data));
oid = nmos::fields::nc::id(found_resource.data);
}

auto object_properties_holder = web::json::value_of({
{ nmos::fields::nc::path, role_path },
{ nmos::fields::nc::values, property_value_holders}
}, true);
std::reverse(role_path.as_array().begin(), role_path.as_array().end());

return role_path;
}

void get_object_property_holder(const nmos::resources& resources, slog::base_gate& gate, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, const nmos::resource& resource, bool recurse, web::json::value& object_properties_holders)
{
using web::json::value;

// Get property_value_holders for this resource
const value property_value_holders = make_property_value_holders(resource, get_control_protocol_class_descriptor);

const auto role_path = get_role_path(resources, resource);

auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path, property_value_holders);

web::json::push_back(object_properties_holders, object_properties_holder);

// Recurse into members
// Recurse into members...if we want to...and the object has them
if (recurse && nmos::is_nc_block(nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(resource.data))))
{
if (resource.data.has_field(nmos::fields::nc::members))
Expand All @@ -1775,8 +1798,8 @@ void get_object_property_holder(const nmos::resources& resources, slog::base_gat

for (const auto& member : members)
{
const auto& oid = nmos::fields::nc::oid(member);
const auto& found = find_resource(resources, utility::s2us(std::to_string(oid)));
const auto& found = find_resource(resources, utility::s2us(std::to_string(nmos::fields::nc::oid(member))));

if (resources.end() != found)
{
get_object_property_holder(resources, gate, get_control_protocol_class_descriptor, *found, recurse, object_properties_holders);
Expand All @@ -1788,6 +1811,40 @@ void get_object_property_holder(const nmos::resources& resources, slog::base_gat
return;
}

std::size_t generate_validation_fingerprint(const nmos::resources& resources, const nmos::resource& resource)
{
// Generate a hash based on structure of the Device Model
size_t hash(0);

nmos::nc_class_id class_id = nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(resource.data));

boost::hash_combine(hash, class_id);
boost::hash_combine(hash, nmos::fields::nc::role(resource.data));

// Recurse into members...if we want to...and the object has them
if (nmos::is_nc_block(nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(resource.data))))
{
if (resource.data.has_field(nmos::fields::nc::members))
{
const auto& members = nmos::fields::nc::members(resource.data);

// Generate hash for block members
for (const auto& member : members)
{
const auto& oid = nmos::fields::nc::oid(member);
const auto& found = find_resource(resources, utility::s2us(std::to_string(oid)));
if (resources.end() != found)
{
size_t sub_hash = generate_validation_fingerprint(resources, *found);
boost::hash_combine(hash, sub_hash);
}
}
}
}

return hash;
}

// Example Device Configuration callback for creating a back-up dataset
nmos::get_properties_by_path_handler make_node_implementation_get_properties_by_path_handler(const nmos::resources& resources, slog::base_gate& gate)
{
Expand All @@ -1804,36 +1861,87 @@ nmos::get_properties_by_path_handler make_node_implementation_get_properties_by_

get_object_property_holder(resources, gate, get_control_protocol_class_descriptor, resource, recurse, object_properties_holders);

auto bulk_values_holder = value_of({
{ nmos::fields::nc::validation_fingerprint, U("your-fingerprint-here")},
{ nmos::fields::nc::values, object_properties_holders}
}, true);
size_t validation_fingerprint = generate_validation_fingerprint(resources, resource);

auto bulk_values_holder = nmos::details::make_nc_bulk_values_holder(utility::string_t(std::to_wstring(validation_fingerprint)), object_properties_holders);

return nmos::details::make_nc_method_result({ nmos::nc_method_status::ok }, bulk_values_holder);
};
}

web::json::value apply_backup_data_set(nmos::resources& resources, nmos::experimental::control_protocol_state& control_protocol_state, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, const nmos::resource& resource, const web::json::value& backup_data_set, bool recurse, const web::json::value& property_traits_, bool validate)
{
auto object_properties_set_validation_values = web::json::value::array();

for (const auto& object_properties_value : nmos::fields::nc::values(backup_data_set))
{
const auto& role_path = nmos::fields::nc::path(object_properties_value);
const auto& found = nmos::find_control_protocol_resource_by_role_path(resources, role_path);

if (resources.end() != found)
{
auto property_restore_notices = web::json::value::array();

nmos::nc_class_id class_id = nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(found->data));

for (const auto& property_value : nmos::fields::nc::values(object_properties_value))
{
const auto& property_id = nmos::details::parse_nc_property_id(nmos::fields::nc::id(property_value));

const auto& property_traits = nmos::fields::nc::property_traits(nmos::find_decorated_property_descriptor(property_id, class_id, get_control_protocol_class_descriptor));
const auto& filtered_traits = boost::copy_range<std::set<web::json::value>>(property_traits.as_array()
| boost::adaptors::filtered([](const web::json::value& property_trait)
{
return property_trait.as_integer() == nmos::nc_property_trait::general;
})
);
if (filtered_traits.size())
{
if (!validate)
{
// modify control protocol resources
const auto& value = nmos::fields::nc::value(property_value);

modify_control_protocol_resource(resources, found->id, [&](nmos::resource& resource)
{
resource.data[nmos::fields::nc::name(property_value)] = value;

}, nmos::make_property_changed_event(nmos::fields::nc::oid(resource.data), { { property_id, nmos::nc_property_change_type::type::value_changed, value } }));
}
}
else
{
const auto& property_restore_notice = nmos::details::make_nc_property_restore_notice(property_id, nmos::fields::nc::name(property_value), nmos::nc_property_restore_notice_type::warning, U("Property not configurable"));
web::json::push_back(property_restore_notices, property_restore_notice);
}
}
const auto& object_properties_set_validation = nmos::details::make_nc_object_properties_set_validation(role_path, nmos::nc_restore_validation_status::ok, property_restore_notices, U("OK"));
web::json::push_back(object_properties_set_validation_values, object_properties_set_validation);
}
}

return nmos::details::make_nc_method_result({ nmos::nc_method_status::ok }, object_properties_set_validation_values);
}

// Example Device Configuration callback for validating a back-up dataset
nmos::validate_set_properties_by_path_handler make_node_implementation_validate_set_properties_by_path_handler(const nmos::resources& resources, slog::base_gate& gate)
nmos::validate_set_properties_by_path_handler make_node_implementation_validate_set_properties_by_path_handler(nmos::resources& resources, slog::base_gate& gate)
{
return [&resources, &gate](nmos::experimental::control_protocol_state& control_protocol_state, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, const nmos::resource& resource, const web::json::value& backup_data_set, bool recurse, const web::json::array& included_property_traits)
return [&resources, &gate](nmos::experimental::control_protocol_state& control_protocol_state, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, const nmos::resource& resource, const web::json::value& backup_data_set, bool recurse, const web::json::value& property_traits)
{
slog::log<slog::severities::info>(gate, SLOG_FLF) << nmos::stash_category(impl::categories::node_implementation) << "Do validate_set_properties_by_path";

// Can this backup be restored?
return nmos::details::make_nc_method_result({ nmos::nc_method_status::ok });
return apply_backup_data_set(resources, control_protocol_state, get_control_protocol_class_descriptor, resource, backup_data_set, recurse, property_traits, true);
};
}

// Example Device Configuration callback for restoring a back-up dataset
nmos::set_properties_by_path_handler make_node_implementation_set_properties_by_path_handler(nmos::resources& resources, slog::base_gate& gate)
{
return [&resources, &gate](nmos::experimental::control_protocol_state& control_protocol_state, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, const nmos::resource& resource, const web::json::value& data_set, bool recurse, const web::json::array& included_property_traits)
return [&resources, &gate](nmos::experimental::control_protocol_state& control_protocol_state, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, const nmos::resource& resource, const web::json::value& backup_data_set, bool recurse, const web::json::value& property_traits)
{
slog::log<slog::severities::info>(gate, SLOG_FLF) << nmos::stash_category(impl::categories::node_implementation) << "Do set_properties_by_path";

// Implement restore of device model here
return nmos::details::make_nc_method_result({ nmos::nc_method_status::ok });
return apply_backup_data_set(resources, control_protocol_state, get_control_protocol_class_descriptor, resource, backup_data_set, recurse, property_traits, false);
};
}

Expand Down
Loading

0 comments on commit 9e5e604

Please sign in to comment.