diff --git a/Development/nmos-cpp-node/node_implementation.cpp b/Development/nmos-cpp-node/node_implementation.cpp index 56291cd9..37921cd0 100644 --- a/Development/nmos-cpp-node/node_implementation.cpp +++ b/Development/nmos-cpp-node/node_implementation.cpp @@ -852,7 +852,7 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr auto connection_sender = nmos::make_connection_events_websocket_sender(sender_id, device_id, source_id, model.settings); connection_sender.data[nmos::fields::endpoint_active][nmos::fields::master_enable] = connection_sender.data[nmos::fields::endpoint_staged][nmos::fields::master_enable] = value::boolean(true); resolve_auto(sender, connection_sender, connection_sender.data[nmos::fields::endpoint_active][nmos::fields::transport_params]); - nmos::set_resource_subscription(sender, nmos::fields::master_enable(connection_sender.data[nmos::fields::endpoint_active]), {}, nmos::tai_now()); + nmos::set_resource_subscription(model.node_resources, sender, nmos::fields::master_enable(connection_sender.data[nmos::fields::endpoint_active]), {}, nmos::tai_now()); if (!insert_resource_after(delay_millis, model.node_resources, std::move(source), gate)) throw node_implementation_init_exception(); if (!insert_resource_after(delay_millis, model.node_resources, std::move(flow), gate)) throw node_implementation_init_exception(); diff --git a/Development/nmos/activation_utils.cpp b/Development/nmos/activation_utils.cpp index 31d0dfb1..235f8751 100644 --- a/Development/nmos/activation_utils.cpp +++ b/Development/nmos/activation_utils.cpp @@ -226,12 +226,12 @@ namespace nmos } } - modify_resource(resources, id_type.first, [&response_activation](nmos::resource& resource) + modify_resource(resources, id_type.first, [&resources, &response_activation](nmos::resource& resource) { auto& staged = nmos::fields::endpoint_staged(resource.data); auto& staged_activation = staged[nmos::fields::activation]; - resource.data[nmos::fields::version] = web::json::value::string(nmos::make_version()); + resource.data[nmos::fields::version] = value::string(nmos::make_version(nmos::strictly_increasing_update(resources))); // "For immediate activations, [the `mode` field] will be null in response to any subsequent GET requests." staged_activation[nmos::fields::mode] = value::null(); diff --git a/Development/nmos/api_utils.cpp b/Development/nmos/api_utils.cpp index a4fa44be..0e161d19 100644 --- a/Development/nmos/api_utils.cpp +++ b/Development/nmos/api_utils.cpp @@ -503,8 +503,13 @@ namespace nmos nmos::api_gate gate(gate_, req, parameters); const auto received_time = req.headers().find(details::received_time); + const auto processing_dur = req.headers().end() != received_time - ? bst::chrono::duration_cast(nmos::tai_clock::now() - nmos::time_point_from_tai(nmos::parse_version(received_time->second))).count() / 1000.0 + ? bst::chrono::duration_cast( + // processing_dur = now - received time + ( tai_clock::time_point(tai_clock::duration(std::chrono::duration_cast(std::chrono::steady_clock::now().time_since_epoch()).count())) + - nmos::time_point_from_tai(nmos::parse_version(received_time->second))) + ).count() / 1000.0 : 0.0; // experimental extension, to add Server-Timing response header @@ -707,15 +712,12 @@ namespace nmos add_api_finally_handler(api_, hsts, gate); auto api = [api_, &gate](web::http::http_request req) mutable { - // hmm, in Windows, the boost version of the time_point::now sometimes returns the same value after a small time increment. - // This issue does not seem to happen in the std::chrono implementation. -#if defined(_WIN32) && defined (BST_THREAD_BOOST) - // calculate received_time using std::chrono - const auto now = tai_clock::time_point(tai_clock::duration(std::chrono::system_clock::now().time_since_epoch().count() + tai_clock::tai_offset().count())); + // hmm, in Windows, the boost version of time_point::now (system_clock::now) sometimes returns the same value after a small time increment. + // I guess the same could happen with the Boost version of steady_clock::now, since this issue does not seem to occur in the std::chrono implementation. + // So let's use std::chrono for both Windows and Linux. + const auto now = tai_clock::time_point(tai_clock::duration(std::chrono::steady_clock::now().time_since_epoch().count())); req.headers().add(details::received_time, nmos::make_version(tai_from_time_point(now))); -#else - req.headers().add(details::received_time, nmos::make_version()); -#endif + slog::log(gate, SLOG_FLF) << stash_remote_address(req.remote_address()) << stash_http_method(req.method()) diff --git a/Development/nmos/channelmapping_activation.cpp b/Development/nmos/channelmapping_activation.cpp index 7aabd443..a7ad2b1c 100644 --- a/Development/nmos/channelmapping_activation.cpp +++ b/Development/nmos/channelmapping_activation.cpp @@ -149,9 +149,9 @@ namespace nmos if (!source_id_or_null.is_null()) { - nmos::modify_resource(model.node_resources, source_id_or_null.as_string(), [&activation_time](nmos::resource& source) + nmos::modify_resource(model.node_resources, source_id_or_null.as_string(), [&resources = model.node_resources, &activation_time](nmos::resource& source) { - nmos::set_resource_version(source, activation_time); + nmos::set_resource_version(resources, source, activation_time); }); } @@ -188,9 +188,9 @@ namespace nmos for (auto device = devices.first; devices.second != device; ++device) { - nmos::modify_resource(model.node_resources, device->id, [&activation_time](nmos::resource& source) + nmos::modify_resource(model.node_resources, device->id, [&resources = model.node_resources, &activation_time](nmos::resource& source) { - nmos::set_resource_version(source, activation_time); + nmos::set_resource_version(resources, source, activation_time); }); } } diff --git a/Development/nmos/channelmapping_api.cpp b/Development/nmos/channelmapping_api.cpp index 0439d8a1..f02a86d4 100644 --- a/Development/nmos/channelmapping_api.cpp +++ b/Development/nmos/channelmapping_api.cpp @@ -558,14 +558,13 @@ namespace nmos // Update the IS-04 source or device after the active map is changed in any way // (This function should be called after nmos::set_channelmapping_output_active.) - void set_resource_version(nmos::resource& node_resource, const nmos::tai& activation_time) + void set_resource_version(const nmos::resources& node_resources, nmos::resource& node_resource, const nmos::tai& activation_time) { using web::json::value; auto& resource = node_resource; - const auto at = value::string(nmos::make_version(activation_time)); - resource.data[nmos::fields::version] = at; + resource.data[nmos::fields::version] = value::string(nmos::make_version(nmos::strictly_increasing_update(node_resources, activation_time))); } web::http::experimental::listener::api_router make_unmounted_channelmapping_api(nmos::node_model& model, details::channelmapping_output_map_validator validate_merged, slog::base_gate& gate_) diff --git a/Development/nmos/channelmapping_api.h b/Development/nmos/channelmapping_api.h index 04cad01d..afa97b28 100644 --- a/Development/nmos/channelmapping_api.h +++ b/Development/nmos/channelmapping_api.h @@ -52,7 +52,7 @@ namespace nmos // Update the IS-04 source or device after the active map is changed in any way // (This function should be called after nmos::set_channelmapping_output_active.) - void set_resource_version(nmos::resource& node_resource, const nmos::tai& activation_time); + void set_resource_version(const nmos::resources& node_resources, nmos::resource& node_resource, const nmos::tai& activation_time); // Channel Mapping API implementation details namespace details diff --git a/Development/nmos/connection_activation.cpp b/Development/nmos/connection_activation.cpp index eaa022b0..dbd16b09 100644 --- a/Development/nmos/connection_activation.cpp +++ b/Development/nmos/connection_activation.cpp @@ -142,7 +142,7 @@ namespace nmos { // Update the IS-05 resource's /active endpoint - nmos::set_connection_resource_active(connection_resource, [&](web::json::value& endpoint_active) + nmos::set_connection_resource_active(model.connection_resources, connection_resource, [&](web::json::value& endpoint_active) { // the resolve_auto callback may throw exceptions, which will prevent activation in order that // "if there is an error condition that means `auto` cannot be resolved, the active transport parameters @@ -169,9 +169,9 @@ namespace nmos // Update the IS-04 resource's subscription - nmos::modify_resource(model.node_resources, id_type.first, [&activation_time, &active, &connected_id](nmos::resource& resource) + nmos::modify_resource(model.node_resources, id_type.first, [&resources = model.node_resources, &activation_time, &active, &connected_id](nmos::resource& resource) { - nmos::set_resource_subscription(resource, active, connected_id, activation_time); + nmos::set_resource_subscription(resources, resource, active, connected_id, activation_time); }); // Synchronous notification that the active parameters for the specified (IS-04/IS-05) sender/connection_sender or receiver/connection_receiver have changed diff --git a/Development/nmos/connection_api.cpp b/Development/nmos/connection_api.cpp index eb63bcaf..445e398f 100644 --- a/Development/nmos/connection_api.cpp +++ b/Development/nmos/connection_api.cpp @@ -555,9 +555,9 @@ namespace nmos // Finally, update the staged endpoint - modify_resource(resources, id_type.first, [&merged](nmos::resource& resource) + modify_resource(resources, id_type.first, [&resources, &merged](nmos::resource& resource) { - resource.data[nmos::fields::version] = web::json::value::string(nmos::make_version()); + resource.data[nmos::fields::version] = value::string(nmos::make_version(nmos::strictly_increasing_update(resources))); nmos::fields::endpoint_staged(resource.data) = merged; // In the case of an immediate activation, this is not yet a valid response @@ -709,7 +709,7 @@ namespace nmos } // Activate an IS-05 sender or receiver by transitioning the 'staged' settings into the 'active' resource - void set_connection_resource_active(nmos::resource& connection_resource, std::function resolve_auto, const nmos::tai& activation_time) + void set_connection_resource_active(const nmos::resources& connection_resources, nmos::resource& connection_resource, std::function resolve_auto, const nmos::tai& activation_time) { using web::json::value; using web::json::value_of; @@ -717,7 +717,7 @@ namespace nmos auto& resource = connection_resource; const auto at = value::string(nmos::make_version(activation_time)); - resource.data[nmos::fields::version] = at; + resource.data[nmos::fields::version] = value::string(nmos::make_version(nmos::strictly_increasing_update(connection_resources, activation_time))); auto& staged = nmos::fields::endpoint_staged(resource.data); auto& staged_activation = staged[nmos::fields::activation]; @@ -776,13 +776,12 @@ namespace nmos // Update the IS-04 sender or receiver after the active connection is changed in any way // (This function should be called after nmos::set_connection_resource_active.) - void set_resource_subscription(nmos::resource& node_resource, bool active, const nmos::id& connected_id, const nmos::tai& activation_time) + void set_resource_subscription(const nmos::resources& node_resources, nmos::resource& node_resource, bool active, const nmos::id& connected_id, const nmos::tai& activation_time) { using web::json::value; using web::json::value_of; auto& resource = node_resource; - const auto at = value::string(nmos::make_version(activation_time)); // "The 'receiver_id' key MUST be set to `null` in all cases except where a unicast push-based Sender is configured to transmit to an NMOS Receiver, and the 'active' key is set to 'true'." // "The 'sender_id' key MUST be set to `null` in all cases except where the Receiver is currently configured to receive from an NMOS Sender, and the 'active' key is set to 'true'. @@ -792,7 +791,7 @@ namespace nmos // "When the 'active' parameters of a Sender or Receiver are modified, or when a re-activation of the same parameters // is performed, the 'version' attribute of the relevant IS-04 Sender or Receiver must be incremented." // See https://specs.amwa.tv/is-05/releases/v1.0.0/docs/3.1._Interoperability_-_NMOS_IS-04.html#version-increments - resource.data[nmos::fields::version] = at; + resource.data[nmos::fields::version] = value::string(nmos::make_version(nmos::strictly_increasing_update(node_resources, activation_time))); // Senders indicate the connected receiver_id, receivers indicate the connected sender_id // (depending on the API version) diff --git a/Development/nmos/connection_api.h b/Development/nmos/connection_api.h index bc6b2418..545a7bf5 100644 --- a/Development/nmos/connection_api.h +++ b/Development/nmos/connection_api.h @@ -3,6 +3,7 @@ #include "cpprest/api_router.h" #include "nmos/id.h" +#include "nmos/resources.h" namespace slog { @@ -57,7 +58,7 @@ namespace nmos // Functions for interaction between the Connection API implementation and the connection activation thread // Activate an IS-05 sender or receiver by transitioning the 'staged' settings into the 'active' resource - void set_connection_resource_active(nmos::resource& connection_resource, std::function resolve_auto, const nmos::tai& activation_time); + void set_connection_resource_active(const nmos::resources& connection_resources, nmos::resource& connection_resource, std::function resolve_auto, const nmos::tai& activation_time); // Clear any pending activation of an IS-05 sender or receiver // (This function should not be called after nmos::set_connection_resource_active.) @@ -65,7 +66,7 @@ namespace nmos // Update the IS-04 sender or receiver after the active connection is changed in any way // (This function should be called after nmos::set_connection_resource_active.) - void set_resource_subscription(nmos::resource& node_resource, bool active, const nmos::id& connected_id, const nmos::tai& activation_time); + void set_resource_subscription(const nmos::resources& node_resources, nmos::resource& node_resource, bool active, const nmos::id& connected_id, const nmos::tai& activation_time); // Helper functions for the Connection API callbacks diff --git a/Development/nmos/connection_events_activation.cpp b/Development/nmos/connection_events_activation.cpp index 8bea107e..93fcde97 100644 --- a/Development/nmos/connection_events_activation.cpp +++ b/Development/nmos/connection_events_activation.cpp @@ -169,9 +169,9 @@ namespace nmos { using web::json::value; - const auto at = web::json::value::string(nmos::make_version(activation_time)); + const auto at = value::string(nmos::make_version(activation_time)); - connection_resource.data[nmos::fields::version] = at; + connection_resource.data[nmos::fields::version] = value::string(nmos::make_version(nmos::strictly_increasing_update(model.connection_resources, activation_time))); auto& endpoint_active = nmos::fields::endpoint_active(connection_resource.data); auto& active_activation = endpoint_active[nmos::fields::activation]; @@ -185,9 +185,9 @@ namespace nmos // Update the IS-04 resource's subscription - nmos::modify_resource(model.node_resources, id_type.first, [&activation_time](nmos::resource& resource) + nmos::modify_resource(model.node_resources, id_type.first, [&resources = model.node_resources, &activation_time](nmos::resource& resource) { - nmos::set_resource_subscription(resource, false, {}, activation_time); + nmos::set_resource_subscription(resources, resource, false, {}, activation_time); }); } }; diff --git a/Development/nmos/lldp_handler.cpp b/Development/nmos/lldp_handler.cpp index 4722dc9a..b3f9d488 100644 --- a/Development/nmos/lldp_handler.cpp +++ b/Development/nmos/lldp_handler.cpp @@ -25,6 +25,7 @@ namespace nmos const auto& interface_name = interface->second.name; using web::json::value_of; + using web::json::value; auto lock = model.write_lock(); auto& resources = model.node_resources; @@ -55,9 +56,9 @@ namespace nmos { slog::log(gate, SLOG_FLF) << "LLDP received - updating attached network device info for network interface " << interface->second.name; - nmos::modify_resource(resources, resource->id, [&interface_name, &attached_network_device](nmos::resource& resource) + nmos::modify_resource(resources, resource->id, [&resources, &interface_name, &attached_network_device](nmos::resource& resource) { - resource.data[nmos::fields::version] = web::json::value::string(nmos::make_version()); + resource.data[nmos::fields::version] = value::string(nmos::make_version(nmos::strictly_increasing_update(resources))); auto& json_interfaces = nmos::fields::interfaces(resource.data); diff --git a/README.md b/README.md index 25967f0e..1490694e 100644 --- a/README.md +++ b/README.md @@ -139,6 +139,7 @@ The implementation is designed to be extended. Development is ongoing, following Recent activity on the project (newest first): +- Added Network Time Protocol (NTP) support - Added support for IS-14 NMOS Device Configuration - Added support for BCP-008-01 Receiver Status Monitoring - Added support for BCP-008-02 Sender Status Monitoring